I notice that @use_kwargs in flask-apispec changes the response content-type. In the following "hello world" example, The use of @use_kwargs changes the response content-type from text/html to application/json. I find it a bit surprising since the flask-apispec doc doesn't mention it and I wouldn't expect injecting args also changes the response type:
from flask import Flask
from flask_apispec import use_kwargs
from marshmallow import fields
app = Flask(__name__)
@app.route('/')
@use_kwargs({'email': fields.Str()}, location="query")
def hello_world(**kwargs):
return 'Hello, World!
curl -v http://127.0.0.1:5000/\?email\=abc shows the response
> GET /?email=abc HTTP/1.1
> Host: 127.0.0.1:5000
> User-Agent: curl/7.64.1
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: application/json
< Content-Length: 16
< Server: Werkzeug/1.0.1 Python/3.8.2
< Date: Tue, 12 Jan 2021 06:09:25 GMT
<
"Hello, World!"
Note that the Content-Type: application/json and value hello world is quoted. However,
without the line of @use_kwargs, the response is of Content-Type: text/html and the content hello world is not quoted:
~ curl -v http://127.0.0.1:5000/\?email\=abc
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
> GET /?email=abc HTTP/1.1
> Host: 127.0.0.1:5000
> User-Agent: curl/7.64.1
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 13
< Server: Werkzeug/1.0.1 Python/3.8.2
< Date: Tue, 12 Jan 2021 06:09:42 GMT
<
* Closing connection 0
Hello, World!%
What is the rationale of changing the response? How can I set the response content type of "text/html" even with the use of @use_kwargs? Thank you !
Update:
Just add a bit more details on the answer given by @Diego Miguel: The effect of changing content is caused by the logic in Wrapper.call
def __call__(self, *args, **kwargs):
response = self.call_view(*args, **kwargs)
if isinstance(response, werkzeug.Response):
return response
rv, status_code, headers = unpack(response)
mv = self.marshal_result(rv, status_code)
response = packed(mv, status_code, headers)
return flask.current_app.make_response(response)
marshal_result calls flask.jsonify which makes a Response object with "application/json" mimetype and also introduces an extra level of quotation of the "hello world" example above.
I'm not entirely sure why using
@use_kwargschanges the content-type. By looking at the source code, it seems to return adict, judging by this function (that is called byactivate). So my best guess is that Flask when executingapp.routejsonifys thatdict, as that the default behaviour. At that point, thecontent-typeis changed toapplication/json. However,hello_worldis executed afteruse_kwargs, finally returning a string, that is, "Hello World!".In any case, I don't think this behaviour is actually intended by
flask-apispec.You can change the
content-typeof your response (and any other field), creating aFlask.Responseobject withmake_reponseand then setting itscontent-typeto"text/html"(however, this is set by default when passing a string tomake_responseso it's not necessary):Output of
curl -v http://127.0.0.1:5000/\?email\=abc: