Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ServeDir to rewrite path to include .html #383

Open
jdukewich opened this issue Jul 8, 2023 · 1 comment
Open

Allow ServeDir to rewrite path to include .html #383

jdukewich opened this issue Jul 8, 2023 · 1 comment
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. T-middleware Topic: middleware

Comments

@jdukewich
Copy link

Feature Request

Motivation

Next.js Static Export results in a /out directory that looks something like

/out/index.html
/out/register.html
/out/404.html

This differs slightly from a traditional SPA where all routes would go to index.html, as a request to /register should use the register.html file.

There may be a way to accomplish what I'm asking, but I couldn't figure it out from the docs and/or examples.
I tried use ServeDir as so

let app = Router::new().nest_service("/", ServeDir::new("out"));

Going to / correctly returns the index page.
Going to /register.html correctly returns the register page.
But I want /register to also return the register page, but it returns 404

Proposal

For a given path without any extension, attempt to append .html. Similar to append_index_html_on_directories, but could have something such as append_html_on_directories where it would only append ".html" rather than "index.html" to the path.

Alternatives

Not sure of any.

@Pistonight
Copy link

I came up with a workaround that uses a custom service that wraps ServeDir as the fallback. The custom service will always add .html to the request path

use std::convert::Infallible;
use std::task::{Context, Poll};

use axum::http::{Request, Response};
use axum::body::Bytes;
use http_body::Body;
use tower::Service;
use tower_http::services::ServeDir;

/// Service that automatically adding .html extension to requests
#[derive(Debug, Clone)]
pub struct AddHtmlExtService<Fallback>(pub ServeDir<Fallback>);

impl<ReqBody, F, FResBody> Service<Request<ReqBody>> for AddHtmlExtService<F>
where
    F: Service<Request<ReqBody>, Response = Response<FResBody>, Error = Infallible> + Clone,
    F::Future: Send + 'static,
    FResBody: Body<Data = Bytes> + Send + 'static,
    FResBody::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    type Response = <ServeDir<F> as Service<Request<ReqBody>>>::Response;
    type Error = <ServeDir<F> as Service<Request<ReqBody>>>::Error;
    type Future = <ServeDir<F> as Service<Request<ReqBody>>>::Future;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        <ServeDir<F> as Service<Request<ReqBody>>>::poll_ready(&mut self.0, cx)
    }

    fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
        // this removes the scheme and authority, but it's ok since ServeDir doesn't care
        if let Ok(uri) = format!("{}.html", req.uri().path()).parse() {
            *req.uri_mut() = uri;
        }
        self.0.call(req)
    }
}

To compose the service:

let service = ServeDir::new("foo").fallback(AddHtmlExtService(ServeDir::new("foo")))

When this service encounters /register, it cannot find the file, so it will call the fallback. The fallback will call the inner service with /register.html which succeeds.

I don't know if this works for all cases, but from some simple testing it seems to work. Also this can probably be a bit more efficient if it is built in to ServeDir

@jplatte jplatte added C-feature-request Category: A feature request, i.e: not implemented / a PR. T-middleware Topic: middleware labels Nov 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. T-middleware Topic: middleware
Projects
None yet
Development

No branches or pull requests

3 participants