首页 > 解决方案 > @use_kwargs 更改响应内容

问题描述

我注意到@use_kwargsinflask-apispec改变了响应内容类型。在以下“hello world”示例中,使用@use_kwargs将响应内容类型从 更改text/htmlapplication/json。我觉得这有点令人惊讶,因为flask-apispec 文档 没有提到它,而且我不希望注入 args 也会改变响应类型:

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.

标签: flaskflask-apispec

解决方案


我不完全确定为什么 using@use_kwargs会更改内容类型。通过查看源代码,它似乎返回 a dict,由这个函数判断(由 调用activate)。所以我最好的猜测是 Flask 在执行app.routejsonify时将其dict作为默认行为。此时,content-type更改为application/json。然而,hello_world在 之后执行use_kwargs,最终返回一个字符串,即“Hello World!”。

无论如何,我不认为这种行为实际上是由flask-apispec.

您可以更改content-type您的响应(和任何其他字段),创建一个Flask.Response对象,make_reponse然后将其设置content-type"text/html"(但是,这是在将字符串传递给时默认设置的,make_response因此没有必要):

from flask import Flask, make_response
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):
    response = make_response("Hello World!")
    response.content_type = 'text/html'  # can be omitted
    return response

输出curl -v http://127.0.0.1:5000/\?email\=abc

> GET /?email=abc HTTP/1.1
> Host: 127.0.0.1:5000
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html
< Content-Length: 13
< Server: Werkzeug/1.0.1 Python/3.9.1
< Date: Tue, 12 Jan 2021 19:08:05 GMT
< 
Hello World!

推荐阅读