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

OpenAPI not being served according to Actix scopes #283

Open
JasterV opened this issue Sep 14, 2022 · 6 comments
Open

OpenAPI not being served according to Actix scopes #283

JasterV opened this issue Sep 14, 2022 · 6 comments

Comments

@JasterV
Copy link

JasterV commented Sep 14, 2022

Hi!

I'm trying to expose an OpenAPI spec this way:

...
.service(
    web::scope("/docs")
        .wrap(auth)
        .service(
            SwaggerUi::new("/swagger-ui/{_:.*}").url("/api-doc/openapi.json", openapi.clone()),
))

The problem is that when navigating to localhost:8000/docs/swagger-ui we get the following error:

screenshot_2022-09-14_at_11 24 17

And the reason is that the openapi.json is now under /docs/api-doc/openapi.json but by default the UI tries to point to /api-doc/openapi.json.

In fact, if I search on the bar for /docs/api-doc/openapi.json it renders the correct page

@juhaku
Copy link
Owner

juhaku commented Sep 14, 2022

In general the scopes are quite hard topic, and most likely there is some room to improve. It's just a really hard nut to crack since the web libraries are not exposing the context path or scope outside.

As what comes to the example, the SwaggerUi::new(...) expects a relative path from root of the app and similarly the .url(...) method does.

SwaggerUi::new("/swagger-ui/{_:.*}").url("/api-doc/openapi.json", openapi.clone())

For what comes to scopes now maybe this example might help you: https://github.com/juhaku/utoipa/tree/master/examples/actix-web-multiple-api-docs-with-scopes

And there is an old issue regarding the sopes as well: #121
And also there is this PR #274 which is still unresolved but concerning axum framework but concerns same topic and actually if something will be done then the changes here should be also done for actix-web and rocket framework's as well.

@JasterV
Copy link
Author

JasterV commented Sep 14, 2022

@juhaku Thank you, I'm going to try the example

As a workaround, I've done this:

// On the server
web::scope("/docs")
     .wrap(HttpAuthentication::basic(validator))
     .route(
         "/openapi.json",
          web::get().to(crate::controller::open_api_spec),
      )
      .service(
          SwaggerUi::new("swagger/{_:.*}")
          .url("/docs/openapi.json", Default::default()),
);

// open_api_spec
pub async fn open_api_spec(_: web::Data<ServerData>) -> HttpResponse<BoxBody> {
    let openapi = ApiDoc::openapi();
    HttpResponse::Ok().json(openapi)
}

And it actually works 😄 The idea is to expose the json file on a separate route and then point to it with the SwaggerUI service. But this is a bit tricky and we are exposing a dummy default OpenAPI spec

@JasterV
Copy link
Author

JasterV commented Sep 14, 2022

@juhaku The example did not work for us as what we want is to modify the endpoint where the json file is served, that is a bit different from what is being done in the example which is related to modifying the base endpoint of the exposed paths.

I hope it makes sense 😵‍💫 hahah

Thank you anyway!

@juhaku
Copy link
Owner

juhaku commented Sep 14, 2022

Aa yeah, I see.. So you would like to have custom functionality over the handler of the OpenAPI doc endpoint? If I get you right 😄 That is indeed not supported via the SwaggerUi struct unfortunately since that actually creates simple handlers behind the scenes without offering capability to enhance them in anyways. One alternative is to look the warp or tide example (you can find them in the examples folder) of how to implement the manual serving of Swagger UI and OpenAPI doc.

That is something that could be added in future indeed, but needs some desing thinking for to get it right. And in tandem the support should also be added for all the frameworks like axum and rocket not just actix-web.

@ThomasVille
Copy link

Hi everyone!

We've encountered this issue today and this workaround works fine for the moment but here is another idea, why not provide SwaggerUi both:

  • Where to serve the JSON file.
  • Where to look for the JSON file inside Swagger UI.

Maybe something like this?

...
.service(
    web::scope("/docs")
        .wrap(auth)
        .service(
            SwaggerUi::new("/swagger-ui/{_:.*}").url("/api-doc/openapi.json", openapi.clone()).url_override("/docs/api-doc/openapi.json"),
))

Or give it just the scope?

...
.service(
    web::scope("/docs")
        .wrap(auth)
        .service(
            SwaggerUi::new("/swagger-ui/{_:.*}").url("/api-doc/openapi.json", openapi.clone()).scope("/docs"),
))

These solutions introduce a redundancy in the scope definition but at least they allow covering this use-case without resorting to a workaround.

@juhaku
Copy link
Owner

juhaku commented Sep 18, 2024

@ThomasVille There is also another way as elaborated here in more detail #964 (comment). In general you could try to give custom config for the Swagger UI.

let config = Config::from("/api/path/swagger/should/find/openapi.json");
SwaggerUi::new("/swgger-ui/{_:.*}").url("/api-doc/openapi.json", openapi.clone()).config(config);

The is not a bad idea, but the first approach is already there by way of providing custom config.

juhaku added a commit that referenced this issue Oct 22, 2024
Implements wrappers for `ServiceConfig`, `App`, `Scope` of actix-web.
This allows users to create `App` with collecting `paths` and `schemas`
recursively without registering them to `#[openapi(...)]` attribute.

Example of new supported syntax.
```rust
 use actix_web::{get, App};
 use utoipa_actix_web::{scope, AppExt};

 #[derive(utoipa::ToSchema)]
 struct User {
     id: i32,
 }

 #[utoipa::path(responses((status = OK, body = User)))]
 #[get("/user")]
 async fn get_user() -> Json<User> {
     Json(User { id: 1 })
 }

 let (_, mut api) = App::new()
     .into_utoipa_app()
     .service(scope::scope("/api/v1").service(get_user))
     .split_for_parts();
```

Relates #283 Relates #662
Closes #121 Closes #657
juhaku added a commit that referenced this issue Oct 22, 2024
Implements wrappers for `ServiceConfig`, `App`, `Scope` of actix-web.
This allows users to create `App` with collecting `paths` and `schemas`
recursively without registering them to `#[openapi(...)]` attribute.

Example of new supported syntax.
```rust
 use actix_web::{get, App};
 use utoipa_actix_web::{scope, AppExt};

 #[derive(utoipa::ToSchema)]
 struct User {
     id: i32,
 }

 #[utoipa::path(responses((status = OK, body = User)))]
 #[get("/user")]
 async fn get_user() -> Json<User> {
     Json(User { id: 1 })
 }

 let (_, mut api) = App::new()
     .into_utoipa_app()
     .service(scope::scope("/api/v1").service(get_user))
     .split_for_parts();
```

Relates #283 Relates #662
Closes #121 Closes #657
juhaku added a commit that referenced this issue Oct 22, 2024
Implements wrappers for `ServiceConfig`, `App`, `Scope` of actix-web.
This allows users to create `App` with collecting `paths` and `schemas`
recursively without registering them to `#[openapi(...)]` attribute.

Example of new supported syntax.
```rust
 use actix_web::{get, App};
 use utoipa_actix_web::{scope, AppExt};

 #[derive(utoipa::ToSchema)]
 struct User {
     id: i32,
 }

 #[utoipa::path(responses((status = OK, body = User)))]
 #[get("/user")]
 async fn get_user() -> Json<User> {
     Json(User { id: 1 })
 }

 let (_, mut api) = App::new()
     .into_utoipa_app()
     .service(scope::scope("/api/v1").service(get_user))
     .split_for_parts();
```

Relates #283 Relates #662
Closes #121 Closes #657
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants