Git Product home page Git Product logo

Comments (7)

plang-arista avatar plang-arista commented on June 3, 2024 2

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:

  • 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.

from utoipa.

plang-arista avatar plang-arista commented on June 3, 2024 1

The workaround given will not fix the problem with trailing slash redirect from

routing::get(|| async move { axum::response::Redirect::to(&slash_path) }),

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;
  }
}

from utoipa.

lpfy avatar lpfy commented on June 3, 2024

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(())
}

from utoipa.

plang-arista avatar plang-arista commented on June 3, 2024

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.

from utoipa.

lpfy avatar lpfy commented on June 3, 2024

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.

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.

from utoipa.

lpfy avatar lpfy commented on June 3, 2024

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.

Hi @plang-arista, after I correctly set PATH_PREFIX env var to /rusttest/, Swagger UI is working properly now. thanks for your help.

from utoipa.

qrilka avatar qrilka commented on June 3, 2024

The workaround given will not fix the problem with trailing slash redirect from

routing::get(|| async move { axum::response::Redirect::to(&slash_path) }),

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

from utoipa.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.