Priority of paths in Open API 3 - node.js

I'm using an openapi validator (express-openapi-validator) in my Node.js project and can't figure out how to control the order of the paths matched.
If I have 2 paths such as,
/foo/{type}
parameters:
- name: type
schema:
type: string
enum: ['bar', 'bam']
and
/foo/bar
For a request to /foo/bar, the second path is always matched.
How do I control the precedence of this match?

For a request to /foo/bar, the second path is always matched.
This is the correct and expected behavior. OpenAPI Specification states that specific paths must be matched before similar templated paths - see Path Templating Matching. This is not supposed to be configurable, otherwise the behavior would contradict the specification.
To have requests to /foo/bar handled by /foo/{type}, you'll need to remove the /foo/bar path from the API definition.

Related

MVC route to content to avoid other routes from matching?

In my MVC project I have a route like this:
routes.Add("MyRoute", new MyRoute("{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }));
I also have some bundles (showing only constructor with URL here):
new Bundle("~/Scripts/companyname", typeof(JsMinify));
new Bundle("~/Content/themes/companyname/css", typeof(Microsoft.Web.Optimization.CssMinify));
new Bundle("~/Content/skin/css", typeof(Microsoft.Web.Optimization.CssMinify));
new Bundle("~/Content/companyname_css", typeof(Microsoft.Web.Optimization.CssMinify));
new Bundle("~/Content/print_css", typeof(Microsoft.Web.Optimization.CssMinify));
I want those bundles to not be matched by "MyRoute". How can I achieve that?
Found it. This is what RouteCollection.IgnoreRoute is for. I do find the documentation in this area pretty scarce, so I'll add a few details here.
routes.IgnoreRoute("Content/{*pathInfo}");
routes.IgnoreRoute("Scripts/{*pathInfo}");
Those lines tell the routing system to ignore URL:s that start with Content/ and Scripts/ respectively, and has "anything" after the /. The asterisk * in {*pathInfo} means that the pathInfo token should be assigned the remainder of the URL.
I have not been able to determine if the name pathInfo matters, or could be anything, but it appears that the pattern needs a placeholder there, with the leading asterisk, to match anything after the /.

can bodyparser type take an array or wildcard

At the moment, I am having to do this for certain requests to parse specific requests:
app.use(bodyParser.json({ type: 'application/x-amz-json-1.1' }));
I would like to open this out to regular json also but the docs do not mention how.
I haven't tried it, but according to body-parser documentation it supports array and wildcards.
Quote from docs:
The type option is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, type option is passed directly to the type-is library and this can be an extension name (like json), a mime type (like application/json), or a mime type with a wildcard (like / or */json). If a function, the type option is called as fn(req) and the request is parsed if it returns a truthy value. Defaults to application/json.

Swagger node is taking URL phrase as parameter

I'm using swagger-node for REST APIs and I've found an absurd issue here.
Here's my one of GET requests:
GET: /students/{id}/{name}
(id is number)
And now I wrote another request:
GET: /students/routeInfo/{id}
But the 2nd request me following error:
Expected type is number, found type string. Which clearly means 2nd request is trying to "mask" 1st request. Which I think shouldn't be.
Am I doing something wrong or is there any other way to handle this?
In your path definitions, changeChange your 'parameters' type: number for type: string
Your path will look like this:
/students/routeInfo/{id}:
x-swagger-router-controller: yourStudentController
get:
tags:
- Student
description: description
operationId: yourOperation
parameters:
- name: id
in: path
description: description id of routeInfo
required: true
type: string
See more at https://swagger.io/docs/specification/paths-and-operations/
The exact same situation is described in the OpenAPI specification v3.0 document: section Path Templating Matching, 3rd example. It is called ambiguous matching, and "... it's up to the tooling to decide which path to use." The tool, of course, being swagger-node.
It is weird though, isn't it!

How to make optional params name in express route?

Here is below my code of route:-
app.get('/server/lead/get/:id?', leadCtrl.get);
app.get('/server/lead/filter/:filterQuery', leadCtrl.get);
As you see above i am using different route to access same controller method leadCtrl.get.
Now, i want something like route app.get('/server/lead/get/:id?:filter?', leadCtrl.get);. So, i can get params either req.params.id or req.params.filter but only one at a time.
What you asked in the question is not possible in the form that you describe it.
Now, i want something like route
app.get('/server/lead/get/:id?:filter?', leadCtrl.get);. So, i can get
params either req.params.id or req.params.filter but only one at a
time.
Your router would have no way to differentiate those two parameters. If it got a request to /server/lead/get/X then what is X? A filter or an ID?
Your options
You have few solutions here:
You can either keep using two routes like you did before.
You can use a common parameter for both cases as Robert explained in the comments.
Or you can use what seems to me the perfect solution for your use case - named query parameters - just use a route /server/lead/get and use query parameters to pass id and the filter.
Example URLs:
/server/lead/get?id=xxx
/server/lead/get?filterQuery=xxx
You will only have to make sure in your handler that only one of those two are set at a time with something like:
if (req.query.id && req.query.filterQuery) {
// respond with error
}
You can even mix the two if you have app.get('/server/lead/get/:id?') route you can have the id in the route and filterQuery as a query parameter. Now the URLs would be:
/server/lead/get/xxx (for id)
/server/lead/get?filterQuery=xxx (for filter)
For more info see: http://expressjs.com/en/api.html#req.query
Better way
If you follow some REST conventions then you can use:
app.get('/server/lead/:id') for one object with id (not optional)
app.get('/server/lead') for a list of objects (with optional filterQuery passed as a query parameter)
That way you would always know that when you access:
/server/lead/xxx - then it's one object with ID = xxx
/server/lead - then it's a list of any objects
/server/lead?filterQuery=xxx - then it's a list of objects that match the query
If you follow the REST conventions for things like this instead of inventing your own, it would be much easier for you to design the routes and handlers, and it would be much easier for other people to use your system.
You may also want to use plural /server/leads instead of /server/lead which is common with REST. That way it will be more obvious that leads is a list and leads/id is one of its elements.
For more info see:
https://en.wikipedia.org/wiki/Representational_state_transfer
http://www.restapitutorial.com/lessons/whatisrest.html
https://spring.io/understanding/REST
You have to realize that the following two routes match exactly the same:
app.get('/server/lead/get/:id?', leadCtrl.get);
app.get('/server/lead/get/:filter?', leadCtrl.get);
Express doesn't care about how you name the placeholders, so any requests for /server/lead/get/SOMEVALUE will always match the first (the one with :id).
You can add a distinction yourself, by only allowing a parameter to match a particular regular expression. From your code, it looks like :id should match MongoDB ObjectId's, so you can create a specific match for those:
app.get('/server/lead/get/:id([a-fA-F0-9]{24})?', leadCtrl.get);
If SOMEVALUE matches an ObjectId, it will call leadCtrl.get and populate req.params.id. If you also add another router for "the rest", you can also cover the req.params.filter case:
app.get('/server/lead/get/:filter?', leadCtrl.get);
As an aside: you're saying that you're passing JSON to the "filter" routes, in the URL. I would strongly suggest using a POST route for that, and post the JSON as request body content.

Swagger API "required" - how much is this required?

I've created a Swagger (on nodejs/express) test API with this specification (only relevant part):
...
parameters:
- name: name
in: query
required: true
type: string
...
But I can call the url with empty paramter, for example
http://localhost/test?name=
And it works without any problem, throws no exception or any other sign. Why?
If I make a similar call from the terminal via curl or via postman, it works as well. I parsed the query from the request object and found that in this case, the query parameter is interpreted as an empty string.
Making the call via SwaggerUI is different though, as the UI will actually not make the call UNLESS the query field has a value.
Try doing console.log(req.query); in your handler. You will probably see {name: ''}. Which is legitimate, just that the value of name is an empty string.
Look at JSON4 here: Representing null in JSON. So name IS defined, but it's empty.
You will probably need to do a check for empty string values.
I hope this helps!

Resources