Closed andrzejzysko closed 6 years ago
swagger-ui
uses the details from the spec, specifically basePath
and host
. Are you sure this is a swagger-tools
issue?
Yes, I know that it comes from the spec file. (To be clear and precise host
and basePath
are on the top level not in the info
object). The problem is that I'm using Node.js express sub-app
functionality. It means that finally host
part of the url for the API will be different then oryginal spec but swagger-ui
will still use the same url from file.
In my example host
, basePath
, schemes
and path
are:
host: "localhost"
basePath: "/v3/customerManagement"
schemes:
- "http"
paths:
/customer:
So the API will be exposed under this url: http://localhost/v3/customerManagement/customer
but when you use sub-app
functionality the API will be exposed under this url: http://localhost/customerManagement/v3/customerManagement/customer
but swagger-ui
still will use oryginal specification: http://localhost/v3/customerManagement/customer
.
The question is: How to set different url for swagger-ui
or swagger-router
?
If you are telling express
to use a mountpoint
or some other feature that prefixes the actual API paths, you need to make sure your basePath
in the Swagger document corresponds. That's the only way I know how to but I'm open to ideas.
If I change basePath
adding the same prefix as mountpoint
in express
then this prefix will be added 2 times for API (1 from mountpoint
1 from basePath
) and for swagger-ui
only 1 (only from basePath
). I was thinking about adding this prefix to host
instead of basePath
but the swagger file specification wan't be correct.
I'm not sure I understand what "added two times for API" means. Does swagger-tools
miss matching the API requests or is swagger-ui
doing something wrong, or both?
Let's consider simple examples:
I'll put fallowing url
to swagger file specification using schema
, host
and basePath
: http://localhost:8080/basePath/resourcePart
.
Examples:
Use prefix /foo' in
express`.
http://localhost:8080/foo/basePath/resourcePart
swagger-ui
will use: http://localhost:8080/basePath/resourcePart
Use prefix /foo/bar
in express
.
http://localhost:8080/foo/bar/basePath/resourcePart
swagger-ui
will use: http://localhost:8080/basePath/resourcePart
I would like to do something like this:
(...)
app.use(swaggerMetadata(swaggerDoc));
app.use(swaggerValidator());
app.use(swaggerRouter({
controllers: './controllers'
});
swaggerDoc.basePath = '/samePrefixAsInExpress' + swaggerDoc.basePath;
app.use(swaggerUi(swaggerDoc));
(...)
@andrzejzysko I have found and resolved a similar issue myself.
The problem is that you have internal and external views of the paths. As far as swagger-tools
is concerned, it doesn't know it is actually being served under /foo/bar
. The swagger-router
just uses the paths from the swagger yaml as-is; i.e. handling an internal path of /basePath/resourcePart
. But because of the url prefix, the true external path to request that path has /foo/bar
pre-pended to it giving a full path of /foo/bar/basePath/resourcePath
as you state.
Critically, the swaggerUI is created directly from the same yaml file (by default), and still without knowledge of the path prefix of /foo/bar
. So it also shows the exact paths that are in the yaml, but these are the internal paths, not the full external paths as needed for the browser to call them.
Equally, you can't just change the paths in the yaml file to be the full external paths to make the swaggerUI correct, as that would make the router wrong (and you end up with it only responding to an external path of /foo/bar/foo/bar/basePath/...
).
The solution I came up with was:
basePath
in the swagger yaml as the full external base path: /foo/bar/basePath
.
_.cloneDeep()
, or JSON.parse(JSON.stringify(...))
, or just load the file again etc.)
basePath
in the cloned document to remove the prefix used by your mount point e.g remove /foo/bar
.
initializeMiddleware(clonedAndModifiedSwaggerDoc, ...)
swaggerUi(originalSwaggerDoc, ...)
This gives you a working swagger-router
using the internal paths needed because they are running behind a mount point, and a working swagger-ui
using the external paths needed so that the browser can call the full path.
We use JSON file format for the swagger doc and an Express Router for the sub-application, but the general code is essentially (from memory, so may not be exact!):
const router = expresss.Router();
// Load swagger doc (file defined with full external paths)
const originalSwaggerDocWithExternalPaths = require('path/to/swagger.json');
// Clone the doc to allow us to make a version with _internal_ paths for most of the middleware
const clonedSwaggerDocWithInternalPaths = _.cloneDeep(originalSwaggerDoc);
// Remove the the first 8 chars from `basePath` to remove "/foo/bar"
clonedSwaggerDocWithInternalPaths.basePath = basePath.slice(8);
// Initialize swaggerTools middleware with the _internal_ paths version
swaggerTools.initializeMiddleware(clonedSwaggerDocWithInternalPaths, , function (middleware) {
router.use(middleware.swaggerMetadata());
// ...etc...
// Serve the Swagger documents and Swagger UI using the _external_ paths version
router.use(middleware.swaggerUi(originalSwaggerDocWithExternalPaths));
}
// Mount the router under "/foo/bar" to run as a sub-application
app.use('/foo/bar', router);
Hope this helps!
So it seems like the middleware will work as expected but the URI in swagger-ui
is wrong?
@whitlockjc maybe not wrong but static - can not be change independently to middleware where middleware register url
based on mounting point. We have to think about this in swagger v3 because there is only one field servers
instead of host
, basePath
and schemes
.
OpenAPI 3.0 - API Server and Base URL In OpenAPI 3.0, you use the servers array to specify one or more base URLs for your API.
servers
replaces thehost
,basePath
andschemes
keywords used in OpenAPI 2.0.
@farrago thanks for sharing your approach. I was thinking exactly about the same workaround. The 2nd option is to mount sub-apps in the root /
path and make changes in the basePath
. If there is more sub-apps we have to change options for /api-docs
and /docs
to have them unique.
const swaggerUiOptions = {
apiDocs: '/api-docs' + swaggerDoc.basePath,
swaggerUi: '/docs' + swaggerDoc.basePath
};
app.use(middleware.swaggerUi(swaggerUiOptions));
I'm closing this issue as workaround is provided.
Problem
How to change url api path for swaggerUi where using express sub-app.
The goal is to build one app serving others API as sub-app. The sub-apps have common pattern and are served in root app.
sub-app/index.js
swagger.yaml part
root-app
Problem details
The result is that swaggerUi is served under fallowing url:
API is served under fallowing url:
But when you are trying to use swaggerUi to test this API then the url is:
How to change url path in swaggerUi?