Hilzu / express-openapi-validate

Express middleware to validate requests based on an OpenAPI 3 document
Apache License 2.0
76 stars 12 forks source link

Validation problem while parameters uses a $ref #14

Closed anthony-o closed 6 years ago

anthony-o commented 6 years ago

Here is my schema:

openapi: 3.0.1
info:
  title: My API
  version: '1.0.0'

servers:
  - url: http://localhost:3000/api/v1
    description: Development localhost server

paths:
  /cities:
    get:
      summary: Returns a list of cities.
      description: Returns a list of cities matching the request parameters.
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/perPage'
        - name: postalCode
          in: query
          description: Postal code.
          required: false
          schema:
            $ref: '#/components/schemas/City/properties/postalCode'
      responses:
        '200':    # status code
          description: A JSON array of cities
          content:
            application/json:
              schema: 
                type: array
                items: 
                  $ref: '#/components/schemas/City'

components:
  schemas:
    City:
      properties:
        name:
          type: string
          maxLength: 128
        postalCode:
          type: string
          minLength: 5
          maxLength: 5
          pattern: '^\d{5}$'
  parameters:
    page:
      name: page
      in: query
      description: Page number.
      required: false
      schema:
        type: integer
        minimum: 1
        default: 1
    perPage:
      name: perPage
      in: query
      description: Number of items per page.
      required: false
      schema:
        type: integer
        minimum: 1
        default: 100

Here is my router code:

router
  .route('/')
  .get(validator.validate('get', '/cities'), async (req, res, next) => {
    try {
      res.json(await cityService.list(req.query));
    } catch (error) {
      next(error);
    }
  })
;

and my validator.js:

const fs = require('fs');
const { OpenApiValidator } = require('express-openapi-validate');
const jsYaml = require('js-yaml');
var path = require('path');

const openApiDocument = jsYaml.safeLoad(
  fs.readFileSync(path.join(__dirname, '..', 'api.oas3.yaml'), 'utf-8')
);

module.exports = new OpenApiValidator(openApiDocument);

When I try to access my API at http://localhost:3000/api/v1/cities?page=1&perPage=2&postalCode=32165 I have the following error:

{"code":500,"message":"Error while validating request: request.query.page should be integer","stack":"ValidationError: Error while validating request: request.query.page should be integer\n    at validate (/app/node_modules/express-openapi-validate/dist/OpenApiValidator.js:107:29)\n    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n    at next (/app/node_modules/express/lib/router/route.js:137:13)\n    at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3)\n    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n    at /app/node_modules/express/lib/router/index.js:281:22\n    at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)\n    at next (/app/node_modules/express/lib/router/index.js:275:10)\n    at Function.handle (/app/node_modules/express/lib/router/index.js:174:3)\n    at router (/app/node_modules/express/lib/router/index.js:47:12)\n    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n    at trim_prefix (/app/node_modules/express/lib/router/index.js:317:13)\n    at /app/node_modules/express/lib/router/index.js:284:7\n    at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)\n    at next (/app/node_modules/express/lib/router/index.js:275:10)\n    at Function.handle (/app/node_modules/express/lib/router/index.js:174:3)\n    at router (/app/node_modules/express/lib/router/index.js:47:12)\n    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n    at trim_prefix (/app/node_modules/express/lib/router/index.js:317:13)\n    at /app/node_modules/express/lib/router/index.js:284:7\n    at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)\n    at next (/app/node_modules/express/lib/router/index.js:275:10)"}
Hilzu commented 6 years ago

The validator throws the error: request.query.page should be integer. With the default express query string parser the properties are either strings, objects or arrays. It does no magic to turn strings into numbers. That means that with query parameters you can't specify that they are numbers in your openapi document. A workaround is to define your numeric parameters like this:

page:
  name: page
  in: query
  description: Page number.
  required: false
  schema:
    type: string
    pattern: "^[0-9]*$"

and convert the parameters to numbers in your route code with Number(req.query.page).

anthony-o commented 6 years ago

OK... It is just that with other Swagger (v2) validators such as https://github.com/apigee-127/swagger-tools , when you define an Integer, it is checked and validated as an Integer, so I was expecting other validators to behave the same way.

Hilzu commented 6 years ago

That could be done but I'm hesitant in having a validator library modifying the request. If I add a feature like that it would be opt-in.

anthony-o commented 6 years ago

Is it mandatory to modify the request? Couldn't you "just" add specific RegExp validator for Integers, that assumes that req.query will always have strings and check those string to match an Integer, before it goes forward to the controller function?

Hilzu commented 6 years ago

I would find it confusing that the properties would be validated as numbers but the route code gets strings. For consistency the validator should work on the same values that are provided to the route.

FlorianPfisterer commented 5 years ago

For people still seeing this (like I just did): You can use new OpenApiValidator(openApiDocument, { ajvOptions: { coerceTypes: true }}), which will validate numbers, booleans, etc. correctly.

(taken from #42 in this comment).