stoicflame / enunciate

Build-time enhancement tool for Java-based Web services projects
http://enunciate.webcohesion.com/
Other
480 stars 200 forks source link

context root is not applied for swagger ui, only for enunciate docs #1195

Closed AlexandrSokolov closed 4 weeks ago

AlexandrSokolov commented 7 months ago

I've created a demo project: Module Single Module Project

I define rest interface in terms of jakarta annotations: RestApi

Backend: Spring Boot with Resteasy/Jakarta

enunciate config file contains lots of Maven variables (start with ENUNCIATE_ prefix)

There are 2 maven variables, related to context root defined in pom.xml:

    <ENUNCIATE_ROOT_CONTEXT>/custom/context/path</ENUNCIATE_ROOT_CONTEXT>
    <!--  root rest context must also include JakartaWsConfiguration.APPLICATION_PATH-->
    <ENUNCIATE_ROOT_REST_CONTEXT>${ENUNCIATE_ROOT_CONTEXT}/rest</ENUNCIATE_ROOT_REST_CONTEXT>

Those variables are used in enunciate.xml for description, swagger and jaxrs modules as:

  <description>
    <![CDATA[
      <h2>Rest API Documentation</h2>
      <p>Root context: <b>${ENUNCIATE_ROOT_CONTEXT}</b></p>
    ]]>
  </description>
    <swagger
      basePath="${ENUNCIATE_ROOT_REST_CONTEXT}"
      disabled="false">
    </swagger>
    <jaxrs
      groupBy="class"
      disabled="false"
      path-sort-strategy="depth_first"
      datatype-detection="aggressive">
      <application path="${ENUNCIATE_ROOT_REST_CONTEXT}"/>
    </jaxrs>

To build and run application: mvn clean spring-boot:run

The generated docs will be availablee under: http://localhost:8080/custom/context/path/docs/index.html

And it shows /custom/context/path context root correctly in both description and in the Resources section for RestApi resource. But when I open swagger ui: http://localhost:8080/custom/context/path/docs/ui/index.html

And try to execute rest call against the running application, the context path is entirely ignored. The rest call is sent to http://localhost:8080/api instead of the expected: http://localhost:8080//custom/context/path/rest/api

The demo application is pretty simple by its nature, it has minumal logic, you could play with it to check it at runtime.

AlexandrSokolov commented 7 months ago

Short update. In Jakarta/Jax Rs, when @Path annotion is used, the real path always extends the @ApplicationPath

@Component
@ApplicationPath(JakartaWsConfiguration.APPLICATION_PATH)
public class JakartaWsConfiguration extends Application {
  public static final String APPLICATION_PATH = "/rest";
}
@Path(AppRestServerApi.PATH)
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
public interface AppRestServerApi {

  String PATH = "/demo";
...
}

It means that the rest service will be avaible under: @ApplicationPath/@Path

In our case under /rest/demo

What I try to say, now enunciate entirely ignores the @ApplicationPath and I solve it with a hack/ additional ENUNCIATE_ROOT_REST_CONTEXT variable:

    <ENUNCIATE_ROOT_CONTEXT>/custom/context/path</ENUNCIATE_ROOT_CONTEXT>
    <!--  root rest context must also include JakartaWsConfiguration.APPLICATION_PATH-->
    <ENUNCIATE_ROOT_REST_CONTEXT>${ENUNCIATE_ROOT_CONTEXT}/rest</ENUNCIATE_ROOT_REST_CONTEXT>

The right solution: in enunciate.xml I have to use everywhere only the ${ENUNCIATE_ROOT_CONTEXT} variable. During docs/swagger/jaxrs generation not only the @Path annotation must be used, but also @ApplicationPath

I suppose that for òld javax.ws.rs.ApplicationPath you have this logic. You only need to use it also for new jakarta.ws.rs.ApplicationPath

If you have any questions, please let me know.

stoicflame commented 4 weeks ago

(So sorry it's taken me so long to get to this.)

You need to use the <server> configuration element to tell Swagger where the application path is. Here's a diff I applied your project which demonstrates correct behavior:

diff --git a/src/main/resources/doc/enunciate/enunciate.xml b/src/main/resources/doc/enunciate/enunciate.xml
index f1972f3..535f50b 100644
--- a/src/main/resources/doc/enunciate/enunciate.xml
+++ b/src/main/resources/doc/enunciate/enunciate.xml
@@ -29,6 +29,7 @@
     <swagger
       basePath="${ENUNCIATE_ROOT_REST_CONTEXT}"
       disabled="false">
+      <server url="${ENUNCIATE_ROOT_REST_CONTEXT}"/>
     </swagger>
     <openapi disabled="false" />
     <jackson
@@ -80,4 +81,4 @@
     <disable name="source-files-not-found"/>
   </warnings>

-</enunciate>
\ No newline at end of file
+</enunciate>

As for the @ApplicationPath annotation, Enunciate still honors that annotation but it only applies to JAXRS applications and isn't honored by Spring Boot, so that why it's not getting applied in this case.