connexion icon indicating copy to clipboard operation
connexion copied to clipboard

Malformed path parameter returns 404 instead of 400

Open starhawking opened this issue 6 years ago • 4 comments

Description

The documentation says that strict_validation should return a 400, however it appears to be returning a 404 instead

Per the documentation at https://connexion.readthedocs.io/en/latest/request.html#parameter-validation

Connexion can apply strict parameter validation for query and form data parameters. When this is enabled, requests that include parameters not defined in the swagger spec return a 400 error.

Expected behaviour

Connexion returns a 400 Bad Request when a parameter does not match the schema

Actual behaviour

Connexion returns a 404 Not Found when a parameter does not match the schema

Steps to reproduce

Sample Curl Results

connexion issue> curl -i localhost:5000/has-param/1
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 2
Server: Werkzeug/0.14.1 Python/3.6.4
Date: Fri, 09 Nov 2018 19:31:03 GMT

1
connexion issue> curl -i localhost:5000/has-param/a
HTTP/1.0 404 NOT FOUND
Content-Type: application/problem+json
Content-Length: 206
Server: Werkzeug/0.14.1 Python/3.6.4
Date: Fri, 09 Nov 2018 19:30:55 GMT

{
  "detail": "The requested URL was not found on the server.  If you entered the URL manually please check your spelling and try again.",
  "status": 404,
  "title": "Not Found",
  "type": "about:blank"
}

Sample Code

import connexion
from connexion.resolver import RestyResolver

def get_has_param(demoId):
    return demoId

if __name__ == '__main__':
    app = connexion.FlaskApp(__name__)
    app.add_api(
        'request-validation-demo.yaml',
        validate_responses=True,
        strict_validation=True,
    )
    app.run()

Sample OpenAPI v3 Document

openapi: 3.0.0
info:
  title: This should be returning a 400 on an invalid parameter
  version: '0'
paths:
  /has-param/{demoId}:
    get:
      summary: This should be returning a 400 when it gets an invalid parameter, not a 404
      operationId: main.get_has_param
      parameters:
        - name: demoId
          in: path
          required: true
          description: This should be a 400 when invalid
          schema:
            type: integer
      responses:
        200:
          description: Demo Result
          content:
            application/json:
              schema:
                type: integer
        400:
          description: Demo 400
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        404:
          description: Demo 404
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Error:
      required:
        - code
        - message
      properties:
        code:
          type: integer
        message:
          type: string

Additional info:

Output of the commands:

connexion issue> python3 --version
Python 3.6.4
connexion issue> pip3 show connexion | grep "^Version\:"
Version: 2.0.1
connexion issue>

starhawking avatar Nov 09 '18 19:11 starhawking

I think what's happening is that the path parameters generate routes for Flask. The route that's being generated here is something like: /has-param/<demoId:int>: and so Flask is unable to match /has-param/a to this pattern, and returns a 404.

We could drop all types from Flask routes to ensure that parameters make it through to validation - I'm not sure yet what the full implications of that would be. I'll try to play around with it today.

dtkav avatar Nov 09 '18 21:11 dtkav

I just worked through an issue with similar symptoms (I'm guessing it's the same core issue, but someone with a deeper knowledge of connexion may know better).

I added a new path to an existing (and working) project, but was getting 404s in all cases while testing. I narrowed it down to this trivial reproduction case:

Openapi spec:

...
  /games/{game_id}:
    get:
      summary: Game_GET
      description: blah testing
      operationId: main.foo
      parameters:
        - name: game_id
          in: path
          description: ID of the game being played
          required: true
          schema:
            type: number
...

With the corresponding handler:

def foo(game_id):
    return {'hi': game_id}, 200

All calls to .../games/<n> would return a 404. Reading this bug led me to noticed I'd inadvertently specified the parameter type as number rather than integer - when I changed it back to integer all was well.

In my case, I intended the parameter to be an int, so number in the spec was just a mistake. But since number is the way to specify a float per the openapi spec, though, I wonder how one would get connexion routing to work with a float parameter.

jrheling avatar Jan 10 '20 22:01 jrheling

Just a little push since the bug is still present in version 2.9.0. It's not breaking but the wrong error message and status code is very confusing for users of an API.

D3nn3 avatar Jul 19 '21 12:07 D3nn3

Hi @D3nn3, I'm afraid this issue is not at the top of our priority list at the moment. We currently match Flask behavior and while I agree that a 400 error would be better, I don't think a 404 error is completely wrong either. We would definitely welcome a PR though.

Note that this is not related to strict_validation, which applies strict parameter validation for query and form data parameters, but to malformed path parameters. I updated the title of the issue to reflect this. I think @dtkav was correct in his assessment of the underlying issue.

@jrheling routing with numbers is better supported since version 2.9.

RobbeSneyders avatar Jul 19 '21 15:07 RobbeSneyders

Closing this as a wontfix since routing needs to happen before validation, so a 404 error is thrown before reaching validation.

RobbeSneyders avatar Feb 21 '23 22:02 RobbeSneyders