Open qrilka opened 8 months ago
Hi, I've also run into the same issue, but found a workaround.
My setup: Axum server running behind Reverse Proxy, with URL rewrite: "/api" -> "/"
struct PathPrefixAddon;
impl Modify for PathPrefixAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
match env::var("PATH_PREFIX") {
Ok(prefix) => openapi.servers = Some(vec![Server::new(prefix)]),
Err(_) => ()
}
}
}
async fn main() {
#[derive(OpenApi)]
#[openapi(modifiers(&PathPrefixAddon))]
struct ApiDoc;
let app = Router::new()
.merge(SwaggerUi::new("/docs").config(Config::new(["openapi.json"])).url("/docs/openapi.json", ApiDoc::openapi()));
}
The PathPrefixAddon
tricks utoipa to use "/{prefix}" to access the server, which essentially adds a prefix to every endpoint.
The .config(Config::new(["openapi.json"]))
is also important so that the Swagger UI would look for the openapi.json with a path relative to the /docs
.
Note that there are other pitfalls to this setup:
303
for requests to /docs
to redirect it to Location /docs/
. In your reverse-proxy you also need to setup redirect translation. E.g.: in Nginx I had to add proxy_redirect / /api/;
to prepend the proper prefix.tricks utoipa to use "/{prefix}" to access the server, which essentially adds a prefix to every endpoint.
Hi @plang-arista, thanks for your workaround. I can confirm it also works for the Swagger UI part when loading openapi.json on Actix-web, the next problem I am facing is the "Try it out" part still using the wrong endpoint.
for example, my server is http://www.abcd.com/, set Apache reverse proxy to http://www.abcd.com/rusttest/ for Rust Actix-web The swagger UI URL becomes http://www.abcd.com/rusttest/swagger/ The actual API endpoint is http://www.abcd.com/rusttest/api/xxxxABC The "Try it out" in utoipa swagger UI send request to http://www.abcd.com/api/xxxxABC, the prefix "/rusttest" is missing.
Could you please help to see what's wrong in my code? thanks heaps
use dotenv::dotenv;
use actix_cors::Cors;
use actix_web::{App, HttpServer, web::Data};
use std::{error::Error, env};
use utoipa::{OpenApi, Modify, openapi::server::Server};
use utoipa_swagger_ui::{SwaggerUi, Config};
struct PathPrefixAddon;
impl Modify for PathPrefixAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
match env::var("PATH_PREFIX") {
Ok(prefix) => openapi.servers = Some(vec![Server::new(prefix)]),
Err(_) => ()
}
}
}
async fn main() -> Result<(), Box<dyn Error>> {
dotenv().ok();
//Because Apache reverse proxy configuration,
//environment variable need to be set in .env file for PATH_PREFIX
//Auto-generate API documentation using utoipa and utoipa-swagger-ui
#[derive(OpenApi)]
#[openapi(
modifiers(&PathPrefixAddon),
paths(
get_pages,
),
components(schemas(
Page,
))
)]
struct ApiDoc;
let openapi = ApiDoc::openapi();
HttpServer::new(move || {
App::new()
.wrap(Cors::permissive()) // Add this line to enable CORS for all origins
.service(
//Because Apache reverse proxy configuration, we have to use PathPrefixAddon
//It tricks utoipa to use "/{prefix}" to access the server, which essentially adds a prefix to every endpoint.
SwaggerUi::new("/swagger/{_:.*}").config(Config::new(["openapi.json"])).url("/swagger/openapi.json", openapi.clone()),
)
.configure(init_routes) // Route being handled by routes.rs
})
.bind((CONFIG_PARAMETERS.webserver_host.to_string(), CONFIG_PARAMETERS.webserver_port,))?
.run()
.await?;
Ok(())
}
tricks utoipa to use "/{prefix}" to access the server, which essentially adds a prefix to every endpoint.
Hi @plang-arista, thanks for your workaround. I can confirm it also works for the Swagger UI part when loading openapi.json on Actix-web, the next problem I am facing is the "Try it out" part still using the wrong endpoint.
Hi, this is weird, this should work. Can you add logging that you have set your PATH_PREFIX
env var to /rusttest/
as well? You can also just hard-code that value for your test, to be on the safe side.
In the swagger page you should see a new Drop-Down select, labelled "Servers" with single value /rusttest/
. This is how swagger-ui would know that your /api/xxxxABC
api calls would need to be made against /rusttest/
server on the same host, essentially becoming http://www.abcd.com/rusttest/api/xxxxABC
.
If that variable is also properly set up, I'm not sure how to go on.
tricks utoipa to use "/{prefix}" to access the server, which essentially adds a prefix to every endpoint.
Hi @plang-arista, thanks for your workaround. I can confirm it also works for the Swagger UI part when loading openapi.json on Actix-web, the next problem I am facing is the "Try it out" part still using the wrong endpoint.
Hi, this is weird, this should work. Can you add logging that you have set your
PATH_PREFIX
env var to/rusttest/
as well? You can also just hard-code that value for your test, to be on the safe side.In the swagger page you should see a new Drop-Down select, labelled "Servers" with single value
/rusttest/
. This is how swagger-ui would know that your/api/xxxxABC
api calls would need to be made against/rusttest/
server on the same host, essentially becominghttp://www.abcd.com/rusttest/api/xxxxABC
.If that variable is also properly set up, I'm not sure how to go on.
Thanks for the reply. For the current swagger page, it didn't show Drop-Down select, labelled "Servers" with single value /rusttest/. So might be env var problem. As the server is an internal server behind the firewall, so I could not test it over the weekend at home. Will look into this next Monday.
tricks utoipa to use "/{prefix}" to access the server, which essentially adds a prefix to every endpoint.
Hi @plang-arista, thanks for your workaround. I can confirm it also works for the Swagger UI part when loading openapi.json on Actix-web, the next problem I am facing is the "Try it out" part still using the wrong endpoint.
Hi, this is weird, this should work. Can you add logging that you have set your
PATH_PREFIX
env var to/rusttest/
as well? You can also just hard-code that value for your test, to be on the safe side.In the swagger page you should see a new Drop-Down select, labelled "Servers" with single value
/rusttest/
. This is how swagger-ui would know that your/api/xxxxABC
api calls would need to be made against/rusttest/
server on the same host, essentially becominghttp://www.abcd.com/rusttest/api/xxxxABC
.If that variable is also properly set up, I'm not sure how to go on.
Hi @plang-arista, after I correctly set PATH_PREFIX
env var to /rusttest/
, Swagger UI is working properly now. thanks for your help.
The workaround given will not fix the problem with trailing slash redirect from https://github.com/juhaku/utoipa/blob/4d798bc3e79a0991460c14b657b03bd17d08d7df/utoipa-swagger-ui/src/axum.rs#L52
thus if the API is available under prefix /service
as /api
it will work as /service/api/
but /service/api
will give a bad redirect to /api
The workaround given will not fix the problem with trailing slash redirect from
thus if the API is available under prefix
/service
as/api
it will work as/service/api/
but/service/api
will give a bad redirect to/api
Yeah, I've covered that in:
Axum / Utoipa might respond 303 for requests to /docs to redirect it to Location /docs/. In your reverse-proxy you also need to setup redirect translation. E.g.: in Nginx I had to add proxy_redirect / /api/; to prepend the proper prefix.
This is what I have in my nginx reverse proxy to handle that:
server {
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://backend-proxied:5000;
proxy_redirect / /api/;
proxy_buffering off;
proxy_set_header Host $host;
}
}
OpenAPI supports servers but reverse proxy could be configured independently for a particular service sitting behind that proxy.
utoipa
has support for servers but it looks like the traitModify
is static. Is there some way to use X-Forwarded headers like for example it's done in https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/1801 ?