Problem context
I'm experiencing some unexpected behaviour with MarshallingError
, when generating api documentation using flask-restx. I have a custom flask-restx field definition, like below.
class EnumField(StringMixin, Raw):
def __init__(self, enum_type, *args, **kwargs):
super().__init__(*args, **kwargs)
self.enum_type = enum_type
def format(self, value):
try:
return self.enum_type(value)
except ValueError as e:
raise MarshallingError(e)
def output(self, key, obj, **kwargs):
return self.format(obj[key]).value
where enum_type
is something simple like
class DemoEnum(Enum):
a = 'a'
b = 'b'
c = 'c'
This is then packaged inside a restx api.model
, which looks like the following.
model = api.model('Demo', {"name": EnumField(enum_type=DemoEnum, required=True)})
Issue
When I enter an integer into name
, as expected, I'm getting a nice error like below.
{
"errors": {
"name": "1 is not of type 'string'"
},
"message": "Input payload validation failed"
}
However, when I then enter a value that is not in my enum ("d" for instance), the error seems to be caught in my format
definition, however, MarshallingError
isn't hiding all of the internal errors as expected. Here's a short snippet of what is being output.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>MarshallingError: 'string' is not a valid DemoEnum // Werkzeug Debugger</title>
...
Quesions
- Is this expected behaviour?
- Is it possible to return a cleaner error, like the one shown above? My understanding was that my definition of
format
should achieve this?
Full application for testing
from flask_restx import Api, Resource
from flask_restx.fields import Raw, StringMixin, MarshallingError
from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix
from enum import Enum
# =============================================================================
# Custom EnumField and Enum
# =============================================================================
class EnumField(StringMixin, Raw):
def __init__(self, enum_type, *args, **kwargs):
super().__init__(*args, **kwargs)
self.enum_type = enum_type
def format(self, value):
try:
return self.enum_type(value)
except ValueError as e:
raise MarshallingError(e)
def output(self, key, obj, **kwargs):
return self.format(obj[key]).value
class DemoEnum(Enum):
a = 'a'
b = 'b'
c = 'c'
# =============================================================================
# Demo restx model
# =============================================================================
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
api = Api(app, version='1.0', title='Test API', validate=True)
ns = api.namespace('demo')
model = api.model('Demo', {
"name": EnumField(enum_type=DemoEnum, required=True)
})
# =============================================================================
# Api endpoint
# =============================================================================
@ns.route('/')
class Demo(Resource):
@ns.doc('create_demo')
@ns.expect(model, validate=True) # validate checks the input is provided
@ns.marshal_with(model, code=201)
def post(self):
'''Create demo'''
return api.payload
if __name__ == '__main__':
app.run(debug=True)