6

Consider the following:

from flask import Flask
from flask_restplus import Api, Resource, fields

app = Flask(__name__)
api = Api(app)
ns = api.namespace('ns')

payload = api.model('Payload', {
    'a_str': fields.String(required=True),
    'a_date': fields.Date(required=True)
})

@ns.route('/')
class AResource(Resource):

    @ns.expect(payload)
    def post(self):
        pass

If I POST {"a_str": 0, "a_date": "2000-01-01"} I get 400 as expected, because a_str is not a string. However, when I POST {"a_str": "str", "a_date": "asd"} I don't get 400. Here I would like to also get 400, because "asd" is not a common date format.

I looked into the Date class doc and I see that there is a format and a parse method which should check whether the string is in a common date format. However, they do not seem to be called here.

Is there another way how to do this? Currently I am validating the date format by hand, but it seems that fask restplus should be able to do it for me.

mRcSchwering
  • 646
  • 4
  • 18
  • Can you show `curl` request? in my case I don't see any 400 errors(`curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ \ "a_str": 0, \ "a_date": "aa" \ }' 'http://127.0.0.1:5000/ns/'`) – Danila Ganchar May 04 '19 at 10:36
  • I just copy pasted your request. Get 400 because "a_str" is not a string (which is good, I would also like that behaviour for the date) – mRcSchwering May 04 '19 at 19:23
  • I stumbled upon this: https://flask-restplus.readthedocs.io/en/stable/parsing.html. Im not using reqparse here, but may it be that all parsing and serialization is supposed to be done with marshmallow and not with the flask_restplus tools? – mRcSchwering May 04 '19 at 19:25

3 Answers3

4

As @andilabs mentions, it really weird to define expected payload twice. You can define expected payload by using only RequestParser as so:

from flask import Flask, jsonify
from flask_restplus import Api, Resource, fields, reqparse, inputs

app = Flask(__name__)
api = Api(app)
ns = api.namespace('ns')

parser = reqparse.RequestParser()
parser.add_argument('a_str', type=str)
parser.add_argument('a_date', type=inputs.datetime_from_iso8601, required=True)


@ns.route('/')
class AResource(Resource):

    @ns.expect(parser)
    def get(self):

        try:  # Will raise an error if date can't be parsed.
            args = parser.parse_args()  # type `dict`
            return jsonify(args)
        except:  # `a_date` wasn't provided or it failed to parse arguments.
            return {}, 400


if __name__ == '__main__':
    app.run(debug=True)

Test by using curl:

$ curl -XGET -H "Content-type: application/json" -d '{"a_str": "Name", "a_date": "2012-01-01"}' 'http://127.0.0.1:5000/ns/'
{
  "a_date": "Sun, 01 Jan 2012 00:00:00 GMT", 
  "a_str": "Name"
}
Dinko Pehar
  • 3,766
  • 3
  • 14
  • 34
  • this is strange, that you have to define it twice.. once with api.model and once with request parser. why so? – andilabs Jun 01 '20 at 14:29
  • I think `expect` is used in swagger documentation and the other one is used for parsing arguments. But I found example where you can define parser once, I'll update the answer. – Dinko Pehar Jun 02 '20 at 07:09
  • @andilabs I've updated the answer. I hope this suits your needs. It's also better and simpler example – Dinko Pehar Jun 02 '20 at 07:54
  • This solution worked for me. Despite I feel that the `api.model` syntax is a bit more succint code-wise, creating a `reqparse` instance allows for reuse for input validation and documentation. Besides it makes it easier to pass parameters to the swagger interface. (Despite this, I don't know if it's better to maintain models in order to keep a clearer interface between the endpoints and the Domain Logic) – tamanakid Jun 23 '20 at 10:11
3

To validate you can add parameter validate:

@ns.expect(payload, validate=True)

Here is the link to documentation: https://flask-restplus.readthedocs.io/en/stable/swagger.html#the-api-expect-decorator

HelenShy
  • 186
  • 7
0

Step 1: pip install isodate

Step 2: pip install strict-rfc3339

Step 3:

from jsonschema import FormatChecker
api = Api(your_app,format_checker=FormatChecker(formats=("date-time",)))

Step 4:

@api.expect(your_fields, validate=True)

Reference: Open Issue: https://github.com/noirbizarre/flask-restplus/issues/204

Henshal B
  • 139
  • 1
  • 3