I'm having a weird problem; I'm running Flask and I have an API function to cancel memberships. The URL also contains a parameter to set a reason for cancelation. this is a short text, and it can contain extended characters, so the string is URLencoded by the calling party.
In certain cases however, Flask returns a 400 error, before it even reaches my own processing code. For example, the following URL:
curl -X "DELETE" "http://localhost:5000/contracts/C9ABA4AA-834E-4711-91A8-F21057DF693B?date=2015-8-1&canceldate=2015-6-27&booktoday=true&overrideEnddate=true&cancelreason=traslado+a+m%C3%A1s+de+15+km&correctionreason="
Gives me a 400 error: The browser (or proxy) sent a request that this server could not understand. although it seems to be a perfectly valid URL. When I trap the error the underlying error data is:
'ascii' codec can't encode character u'\\xe1' in position 12: ordinal not in range(128)
I found out that all is well when I remove the encoded extended character á (%C3%A1) from the URL string.
I can also solve it by adding this workaround I found elsewhere to my app.init()
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
I understood that this statement forces the Python interpreter to use UTF-8 by default to decode bytestrings, instead of ASCII.
This gives me a solution to my problem, but it feels overly complicated and unnessecary.
So, the real question is, what am I missing here? Is there a setting within Flask that I don't know about that solves this, or can it really be the case that Flask can't handle specific URL encoded strings? I would expect Flask to be able to handle URL encoded parameters by itself, without a workaround in non-library code...
I have no traceback, because Flask will trap it and handle it as an HTTP error and return a 400 HTTP status code.
EDIT: The origin of the exception is in this part of the Flask code (site-packages\flask\app.py):
def full_dispatch_request(self):
"""Dispatches the request and on top of that performs request
pre and postprocessing as well as HTTP exception catching and
error handling.
.. versionadded:: 0.7
"""
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
response = self.make_response(rv)
response = self.process_response(response)
request_finished.send(self, response=response)
return response
The code skips to the except: and returns a 400 error.
Thanks for any pointers you can give me.
Found out the solution; And it seems I left out some essential information in my question that was needed to resolve the issue.
We tried to duplicate the problem in a very simple single-page Flask app, so I could put it up here on StackOverflow as an example. However, the simple Flask testpage worked fine, so we went to look for the differences.
What I totally forgot, and we noticed just then, was that we use the webargs library to parse http parameters.
The parameters were parsed as strings, and there also is a type unicode available in webargs. Changing the type to unicode solved the problem.
@api.route('/contracts/<ppl_mshp_id>', methods=['DELETE'])
@use_args({'date': Arg(type_=str, validate=is_date, required=False),
'canceldate': Arg(type_=str, validate=is_date, required=False),
'cancelreason': Arg(type_=unicode, required=False, default=u""),
'correction': Arg(type_=float, required=False),
'correctionreason': Arg(type_=unicode, required=False, default=u""),
'restitution': Arg(type_=bool, required=False, default=False),
'booktoday': Arg(type_=bool, required=False, default=False),
'overrideEnddate': Arg(type_=bool, required=False, default=False),
})
def cancel_contract(args, ppl_mshp_id):
...
Thank you @ketouem for your time and trouble, and sorry for being incomplete in my question.