Connexion: Vague error detail

Created on 17 Feb 2019  路  10Comments  路  Source: zalando/connexion

Python version 3.6.5
Connexion Version 2.2.0
OpenAPI 3 Spec.

I'm trying to do simple requestBody Validation, it does not give proper error detail.

{
  "detail": "None is not of type 'object'", // When sending an empty json body.
  "status": 400,
  "title": "Bad Request",
  "type": "about:blank"
}

Also the error messages are very vague at property level too, it does not give the exact property name, any idea why?

{
  "detail": "'' is too short'", // The property name is 'xyz' type string and length is 3
  "status": 400,
  "title": "Bad Request",
  "type": "about:blank"
}

Is this a regression?

question

Most helpful comment

We had a similar problem - this might help.

Creating a validators.py module that looks like this.

"""
This module contains custom validators
that override the default connexion behaviour.
"""

import logging

from connexion.problem import problem
from connexion import decorators
from jsonschema import ValidationError

logger = logging.getLogger("connexion.decorators.validation")


class RequestBodyValidator(decorators.validation.RequestBodyValidator):
    """
    This class overrides the default connexion RequestBodyValidator
    so that it returns the complete string representation of the
    error, rather than just returning the error message.

    For more information:
        - https://github.com/zalando/connexion/issues/558
        - https://connexion.readthedocs.io/en/latest/request.html
    """

    def validate_schema(self, data, url):
        if self.is_null_value_valid and is_null(data):
            return None

        try:
            self.validator.validate(data)
        except ValidationError as exception:
            logger.error(
                "{url} validation error: {error}".format(url=url, error=exception),
                extra={"validator": "body"},
            )
            return problem(400, "Bad Request", str(exception))

        return None

In your app.py overwrite the default RequestBodyValidator as follows:

from validators import RequestBodyValidator

# ...

app.add_api(
    "app.yml",
    validator_map={"body": RequestBodyValidator},
)

All 10 comments

@dtkav any thoughts on this?

Hey @peek4y
Please attach a spec, and a curl command that reproduces the issue.
I'm unable to reproduce the issue with the information you have provided.

The spec config.

openapi: 3.0.0
info:
  title: Sample
  version: "1.0"
paths:
  /users:
    post:
      summary: Adds a new user
      operationId: app.postUser
      requestBody:
        required: true
        content:
          application/json: # Media type
            schema: # Request body contents
              $ref: "#/components/schemas/User" # Reference to an object
            example: # Child of media type because we use $ref above
              # Properties of a referenced object
              id: 10
              name: Jessica Smith
      responses:
        "200":
          description: OK

components:
  schemas:
    User: # Schema name
      type: object
      properties:
        id:
          type: integer
          format: int64
          example: 1 # Property example
        name:
          type: string
          minLength: 3
          example: New order
      required:
        - id
        - name
  • Validating empty body:

    • curl: curl -X POST "http://localhost:8080/users" -H "accept: */*" -H "Content-Type: application/json"
    • result: { "detail": "None is not of type 'object'", "status": 400, "title": "Bad Request", "type": "about:blank" }
    • expected: Probably tell how the body passed is not valid, instead of the above message.
  • Validating type:

    • curl: curl -X POST "http://localhost:8080/users" -H "accept: */*" -H "Content-Type: application/json" -d "{\"id\":\"1\",\"name\":\"Evee\"}"
    • result: { "detail": "'1' is not of type 'integer'", "status": 400, "title": "Bad Request", "type": "about:blank" }
    • expected: It should mention what property failed validation.
    • more samples:

      • curl -X POST "http://localhost:8080/users" -H "accept: */*" -H "Content-Type: application/json" -d "{\"id\":1,\"name\":\"Ee\"}"

      • result: { "detail": "'Ee' is too short", "status": 400, "title": "Bad Request", "type": "about:blank" }

@dtkav hey, just wondering if you got a chance to look at this, were you able to reproduce it?

Regarding the empty body, the message is exactly what I would expect it to be.
You have specified that the type of the body must be an object but you have passed null.

Regarding the property validation, I agree it would be nice if the field was mentioned.
Reading through the jsonschema docs, this should be relatively straightforward.

There's a PR that was started to address this issue: https://github.com/zalando/connexion/pull/816
Unfortunately, it needs a bit more work before it's ready to be merged.

I'd encourage you to pick it up and run with it if you have available bandwidth.
I'd be happy to review a pr, and recommend it for merge by folks at zalando once it's up to standards.

@dtkav
Regarding the null / empty body part, I agree with your point, but then I would still expect a readable message, IMO, maybe it's a nice to have?

Sure, I will take a shot at it, soon, and go through the PR as well.

Thanks a lot for the response!

I think it would be good to clean this up. The "None is not of type 'object'" may be clear to a server developer, but it doesn't make much sense to someone who's writing an API client. Connexion should either treat an empty request body the same way as {}, or return an error message that tells the client that the request body is required.

Connexion should either treat an empty request body the same way as {}

We definitely do not want to to this. A null body, and an empty json object are entirely different things.

Perhaps fixing #878 (support requestBody: required) will also address this concern.

That way, if we mark the request body as required, then the error message would be something like "requestBody is required".

Yep, that would work. Thanks for following up on this! I didn't know about the required field on requestBody until today.

We had a similar problem - this might help.

Creating a validators.py module that looks like this.

"""
This module contains custom validators
that override the default connexion behaviour.
"""

import logging

from connexion.problem import problem
from connexion import decorators
from jsonschema import ValidationError

logger = logging.getLogger("connexion.decorators.validation")


class RequestBodyValidator(decorators.validation.RequestBodyValidator):
    """
    This class overrides the default connexion RequestBodyValidator
    so that it returns the complete string representation of the
    error, rather than just returning the error message.

    For more information:
        - https://github.com/zalando/connexion/issues/558
        - https://connexion.readthedocs.io/en/latest/request.html
    """

    def validate_schema(self, data, url):
        if self.is_null_value_valid and is_null(data):
            return None

        try:
            self.validator.validate(data)
        except ValidationError as exception:
            logger.error(
                "{url} validation error: {error}".format(url=url, error=exception),
                extra={"validator": "body"},
            )
            return problem(400, "Bad Request", str(exception))

        return None

In your app.py overwrite the default RequestBodyValidator as follows:

from validators import RequestBodyValidator

# ...

app.add_api(
    "app.yml",
    validator_map={"body": RequestBodyValidator},
)
Was this page helpful?
0 / 5 - 0 ratings