micronaut-openapi
micronaut-openapi copied to clipboard
Custom context-path, swagger-ui, and `res` folder
I am having an issue with rendering the swagger-ui view in my application that uses a custom context-path. I am seeing 401 responses attempting to access some of the resources in the res
folder. Note that when I enter /api/content/swagger/my-application.yml
I correctly see the YAML file.
Steps to Reproduce
My Micronaut version is 3.7.0 and Micronaut-OpenAPI is version 4.5.2
I have an application running on a custom context-path
of /api/content
. I also have the following configurations for swagger:
micronaut:
application:
name: content-api
server:
context-path: /api/content
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /swagger-ui/**
security:
intercept-url-map:
- pattern: /**/swagger-ui/**
httpMethod: GET
access:
- isAnonymous()
- pattern: /**/swagger/**
httpMethod: GET
access:
- isAnonymous()
My openapi.properties
file indicates:
swagger-ui.enabled=true
micronaut.openapi.server.context.path=/api/content
Expected Behaviour
I'm expecting to see the swagger-ui.
Actual Behaviour
My logs indicate 401 responses retrieving the javascript and CSS resources:
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/swagger-ui
DEBUG i.m.s.a.AuthenticationModeCondition - CookieBasedAuthenticationModeCondition is not fulfilled because micronaut.security.authentication is not one of [cookie, idtoken].
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/swagger-ui, no token found.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.AbstractSecurityRule - The given roles [[isAnonymous()]] matched one or more of the required roles [[isAnonymous()]]. Allowing the request
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/swagger-ui. The rule provider io.micronaut.security.rules.ConfigurationInterceptUrlMapRule authorized the request.
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/res/swagger-ui-bundle.js
DEBUG i.m.h.s.netty.RoutingInBoundHandler - No matching route: GET /api/content/res/swagger-ui-bundle.js
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/res/swagger-ui-standalone-preset.js
DEBUG i.m.h.s.netty.RoutingInBoundHandler - No matching route: GET /api/content/res/swagger-ui-standalone-preset.js
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/res/swagger-ui.css
DEBUG i.m.h.s.netty.RoutingInBoundHandler - No matching route: GET /api/content/res/swagger-ui.css
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/res/swagger-ui-bundle.js, no token found.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern exact match found for path [/api/content/res/swagger-ui-bundle.js] and method [GET]. Searching in patterns with no defined method.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern match found for path [/api/content/res/swagger-ui-bundle.js]. Returning unknown.
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/res/swagger-ui.css, no token found.
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/res/swagger-ui-bundle.js. No rule provider authorized or rejected the request.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern exact match found for path [/api/content/res/swagger-ui.css] and method [GET]. Searching in patterns with no defined method.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern match found for path [/api/content/res/swagger-ui.css]. Returning unknown.
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/res/swagger-ui.css. No rule provider authorized or rejected the request.
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/res/swagger-ui-standalone-preset.js, no token found.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern exact match found for path [/api/content/res/swagger-ui-standalone-preset.js] and method [GET]. Searching in patterns with no defined method.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern match found for path [/api/content/res/swagger-ui-standalone-preset.js]. Returning unknown.
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/res/swagger-ui-standalone-preset.js. No rule provider authorized or rejected the request.
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Response 401 - GET /api/content/res/swagger-ui-bundle.js
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Response 401 - GET /api/content/res/swagger-ui-standalone-preset.js
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Response 401 - GET /api/content/res/swagger-ui.css
Environment Information
- Operating System: Windows
- Micronaut Version: 3.7.0
- JDK Version: 17 (Also, I'm using Groovy)
Example Application
Sorry, I can't give this.
Add to config:
micronaut:
router:
static-resources:
swagger-ui-res:
paths: classpath:META-INF/swagger/views/swagger-ui/res
mapping: /res/**
Updated application.yml:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui-res:
paths: classpath:META-INF/swagger/views/swagger-ui/res
mapping: /res/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /swagger-ui/**
Similar 401 errors:
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/swagger-ui
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/swagger-ui, no token found.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.AbstractSecurityRule - The given roles [[isAnonymous()]] matched one or more of the required roles [[isAnonymous()]]. Allowing the request
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/swagger-ui. The rule provider io.micronaut.security.rules.ConfigurationInterceptUrlMapRule authorized the request.
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/res/swagger-ui-bundle.js
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/res/swagger-ui-bundle.js, no token found.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern exact match found for path [/api/content/res/swagger-ui-bundle.js] and method [GET]. Searching in patterns with no defined method.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern match found for path [/api/content/res/swagger-ui-bundle.js]. Returning unknown.
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/res/swagger-ui-bundle.js. No rule provider authorized or rejected the request.
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Response 401 - GET /api/content/res/swagger-ui-bundle.js
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/res/swagger-ui-standalone-preset.js
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/res/swagger-ui.css
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/res/swagger-ui-standalone-preset.js, no token found.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern exact match found for path [/api/content/res/swagger-ui-standalone-preset.js] and method [GET]. Searching in patterns with no defined method.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern match found for path [/api/content/res/swagger-ui-standalone-preset.js]. Returning unknown.
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/res/swagger-ui-standalone-preset.js. No rule provider authorized or rejected the request.
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/res/swagger-ui.css, no token found.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern exact match found for path [/api/content/res/swagger-ui.css] and method [GET]. Searching in patterns with no defined method.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern match found for path [/api/content/res/swagger-ui.css]. Returning unknown.
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/res/swagger-ui.css. No rule provider authorized or rejected the request.
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Response 401 - GET /api/content/res/swagger-ui-standalone-preset.js
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Response 401 - GET /api/content/res/swagger-ui.css
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/res/favicon-32x32.png
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/res/favicon-32x32.png, no token found.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern exact match found for path [/api/content/res/favicon-32x32.png] and method [GET]. Searching in patterns with no defined method.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern match found for path [/api/content/res/favicon-32x32.png]. Returning unknown.
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/res/favicon-32x32.png. No rule provider authorized or rejected the request.
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Response 401 - GET /api/content/res/favicon-32x32.png
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Request GET /api/content/res/favicon-16x16.png
DEBUG i.m.s.t.reader.HttpHeaderTokenReader - Looking for bearer token in Authorization header
DEBUG i.m.s.t.reader.DefaultTokenResolver - Request GET, /api/content/res/favicon-16x16.png, no token found.
DEBUG i.m.security.rules.IpPatternsRule - One or more of the IP patterns matched the host address [0:0:0:0:0:0:0:1]. Continuing request processing.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern exact match found for path [/api/content/res/favicon-16x16.png] and method [GET]. Searching in patterns with no defined method.
DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern match found for path [/api/content/res/favicon-16x16.png]. Returning unknown.
DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /api/content/res/favicon-16x16.png. No rule provider authorized or rejected the request.
DEBUG i.m.h.s.netty.RoutingInBoundHandler - Response 401 - GET /api/content/res/favicon-16x16.png
@bmunyan ok. I'll check it tomorrow
@bmunyan Well, I added the micronaut.server.context-path
property to the resource paths. In addition, I added the ability to customize the context path to the resources. By default, it is set to /res. The final config should look like this:
micronaut:
server:
context-path: /api/content
openapi:
views:
spec: 'swagger-ui.enabled=true'
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /swagger-ui/**
swagger-ui-res:
paths: classpath:META-INF/swagger/views/swagger-ui/res
mapping: /res/**
And in my sample project I see this:
I pulled your branch, built it, added it as a dependency in my project and, with a little fenagling of the micronaut.security.intercept-url-map
, the above configuration worked for me.
micronaut:
application:
name: content-api
server:
context-path: /api/content
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /swagger-ui/**
swagger-ui-res:
paths: classpath:META-INF/swagger/views/swagger-ui/res
mapping: /res/**
security:
intercept-url-map:
- pattern: ${micronaut.server.context-path}/swagger-ui/**
httpMethod: GET
access:
- isAnonymous()
- pattern: ${micronaut.server.context-path}/res/**
httpMethod: GET
access:
- isAnonymous()
- pattern: ${micronaut.server.context-path}/swagger/**
httpMethod: GET
access:
- isAnonymous()
Is there generally a "ballpark" timeframe for when your PR will be merged and released with a new Micronaut version? Thank you for all the help!
@bmunyan I am glad to help. As for the timing, I don't know, because I'm not in the micronaut team :-). As they decide to create a new release - so be it :-)
@altro3 I am fighting a similar problem. I upgraded to the latest version of micronaut and my swagger-ui endpoint stopped working. Looking at the UI it appears that the javascript/css/image files being incorrectly referenced at {url}/res/...
.
It appears that there is a mismatch between the rendering of the index.html
template and the location of the resource files that index.html
uses. index.html
wants them located at {url}/res/...
but the template engine is placing them at META-INF/swagger/views/swagger-ui/res/...
.
Based on the conversation above you seem to be recommending that @bmunyan get around this problem by mapping the static content at META-INF/swagger/views/swagger-ui/res
to resolve at /res
and then also add a security rules for /res
. If you notice @bmunyan already had to add a static-content rule to allow access to swagger-ui/index.html
and given that rule the javascript files are already addressable via the url swagger-ui/res/swagger-ui-bundle.js
. Given this fact why complicate setup by requiring a 3rd static-content rule and a 3rd security rule? Why not simply fix index.html
to access the js/css/image files where they are already located and where rules have been made to allow access?
To press the point further, looking at OpenApiViewConfig.render():165 you set the swaggerUiDir to be swagger-ui
. Then on line 170 you call the copyResources
method with the swagger-ui
as the parent directory. Then in SwaggerUIConfig.DEFAULT_SWAGGER_JS_PATH
you explicitly configure the location of those resource files inside of index.html
to be res/
instead of swagger-ui/res
. IMO they should be configured to use the same location rather than two different locations. (Thus preventing confusion and unneeded configuration)
One other thought. If SwaggerUIConfig.DEFAULT_SWAGGER_JS_PAT was changed from OpenApiViewConfig.RESOURCE_DIR + "/"
to be "./" + OpenApiViewConfig.RESOURCE_DIR + "/"
it would make those url's relative and the problems I described above go away.