feat(actix-files): support multiple fallback directory indexes

This commit is contained in:
imgurbot12 2025-07-25 18:54:32 -07:00
parent b6ad483699
commit 76d75a37f1
3 changed files with 25 additions and 17 deletions

View File

@ -2,6 +2,7 @@
## Unreleased ## Unreleased
- Option to supply multiple index fallbacks now supported.
- Minimum supported Rust version (MSRV) is now 1.75. - Minimum supported Rust version (MSRV) is now 1.75.
## 0.6.6 ## 0.6.6

View File

@ -38,7 +38,7 @@ use crate::{
pub struct Files { pub struct Files {
mount_path: String, mount_path: String,
directory: PathBuf, directory: PathBuf,
index: Option<String>, indexes: Vec<String>,
show_index: bool, show_index: bool,
redirect_to_slash: bool, redirect_to_slash: bool,
default: Rc<RefCell<Option<Rc<HttpNewService>>>>, default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
@ -61,7 +61,7 @@ impl Clone for Files {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
directory: self.directory.clone(), directory: self.directory.clone(),
index: self.index.clone(), indexes: self.indexes.clone(),
show_index: self.show_index, show_index: self.show_index,
redirect_to_slash: self.redirect_to_slash, redirect_to_slash: self.redirect_to_slash,
default: self.default.clone(), default: self.default.clone(),
@ -108,7 +108,7 @@ impl Files {
Files { Files {
mount_path: mount_path.trim_end_matches('/').to_owned(), mount_path: mount_path.trim_end_matches('/').to_owned(),
directory: dir, directory: dir,
index: None, indexes: Vec::new(),
show_index: false, show_index: false,
redirect_to_slash: false, redirect_to_slash: false,
default: Rc::new(RefCell::new(None)), default: Rc::new(RefCell::new(None)),
@ -192,15 +192,19 @@ impl Files {
self self
} }
/// Set index file /// Set an index file
/// ///
/// Shows specific index file for directories instead of /// Shows specific index file for directories instead of
/// showing files listing. /// showing files listing.
/// ///
/// This function can be called multiple times to configure
/// a list of index fallbacks with their priority set to the
/// order of their addition.
///
/// If the index file is not found, files listing is shown as a fallback if /// If the index file is not found, files listing is shown as a fallback if
/// [`Files::show_files_listing()`] is set. /// [`Files::show_files_listing()`] is set.
pub fn index_file<T: Into<String>>(mut self, index: T) -> Self { pub fn index_file<T: Into<String>>(mut self, index: T) -> Self {
self.index = Some(index.into()); self.indexes.push(index.into());
self self
} }
@ -357,7 +361,7 @@ impl ServiceFactory<ServiceRequest> for Files {
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
let mut inner = FilesServiceInner { let mut inner = FilesServiceInner {
directory: self.directory.clone(), directory: self.directory.clone(),
index: self.index.clone(), indexes: self.indexes.clone(),
show_index: self.show_index, show_index: self.show_index,
redirect_to_slash: self.redirect_to_slash, redirect_to_slash: self.redirect_to_slash,
default: None, default: None,

View File

@ -29,7 +29,7 @@ impl Deref for FilesService {
pub struct FilesServiceInner { pub struct FilesServiceInner {
pub(crate) directory: PathBuf, pub(crate) directory: PathBuf,
pub(crate) index: Option<String>, pub(crate) indexes: Vec<String>,
pub(crate) show_index: bool, pub(crate) show_index: bool,
pub(crate) redirect_to_slash: bool, pub(crate) redirect_to_slash: bool,
pub(crate) default: Option<HttpService>, pub(crate) default: Option<HttpService>,
@ -141,7 +141,7 @@ impl Service<ServiceRequest> for FilesService {
if path.is_dir() { if path.is_dir() {
if this.redirect_to_slash if this.redirect_to_slash
&& !req.path().ends_with('/') && !req.path().ends_with('/')
&& (this.index.is_some() || this.show_index) && (!this.indexes.is_empty() || this.show_index)
{ {
let redirect_to = format!("{}/", req.path()); let redirect_to = format!("{}/", req.path());
@ -152,15 +152,18 @@ impl Service<ServiceRequest> for FilesService {
)); ));
} }
match this.index { let index = this
Some(ref index) => { .indexes
let named_path = path.join(index); .iter()
match NamedFile::open_async(named_path).await { .map(|i| path.join(i))
Ok(named_file) => Ok(this.serve_named_file(req, named_file)), .find(|p| p.exists());
Err(_) if this.show_index => Ok(this.show_index(req, path)),
Err(err) => this.handle_err(err, req).await, match index {
} Some(ref named_path) => match NamedFile::open_async(named_path).await {
} Ok(named_file) => Ok(this.serve_named_file(req, named_file)),
Err(_) if this.show_index => Ok(this.show_index(req, path)),
Err(err) => this.handle_err(err, req).await,
},
None if this.show_index => Ok(this.show_index(req, path)), None if this.show_index => Ok(this.show_index(req, path)),
None => Ok(ServiceResponse::from_err( None => Ok(ServiceResponse::from_err(
FilesError::IsDirectory, FilesError::IsDirectory,