How to customize the response class of flask (customizing-the-flask-response-class)

Response is a class in Flask that responds to client requests. However, Response is rarely used directly in Flask applications. Flask uses Response as a container for response data, and adds some additional information needed to create HTTP responses when responding to client requests. The response data of flask is returned to the client by the routing function of the application. However, Flask also provides an option for applications to allow developers to define some response classes themselves. This article will take advantage of this to show how to simplify your app’s code.

How does Flask’s Response work?

Most applications will not use Flask’s Response class directly, but this does not mean that Response is not used in the framework. In fact, Flask creates a response object for each request. So the question is, how does it work?
The starting moment of the response cycle is the point in time when Flask calls a function to process the return value of the request. In web applications, the render_template function will be called at the end of routing processing. The main function of render_template is to render the referenced template file and return it to the client as a string:

@app.route('/index')
def index():
    #...
    return render_template('index.html')

But as you probably know, a Flask route handler returns two optional additional parameters, one is the encoding of the response status, and the other is a custom HTTP message type:

@app.route('/data')
def index():
    #...
    return render_template('data.json'), 201, {<!-- -->'Content-Type': 'application/json'}

Flask’s default Content-Type value is HTML, and the default encoding value for successful request processing is 200. In the above example, Flaks sets the status encoding to 201 and the response content type to JSON format.
The content of the response consists of three basic units: data or html body, status code, and Http message type. The make_response() function in the Flask application instance implements the function of storing the return value in the routing function in the Response object.
You can enter the following code in the Python console to observe the above process:

>>> from flask import Flask
>>> app = Flask(__name__)
>>> app.make_response('Hello, World')
<Response 12 bytes [200 OK]>
>>> app.make_response(('Hello, World', 201))
<Response 12 bytes [201 CREATED]>

Here I created a Flask application instance, and called the make_response() method to create two Response objects. In the first example, a string is used as a parameter, and the status code and message type in the response use default values. In the second example, the parameter is a tuple parameter containing two values, and sets the status code to a non-default value.
While Flask uses the Response object as the response of the routing function, there are many details that can be handled. For example, the after_request function can be used to insert or modify the http message type, change the html content or status code, and even customize a completely different response class to replace the default Response. Finally, Flask will return the modified response object to the client.

Flask’s Response class

Let’s take a look at the core part of the response class. The following class gives what I think are the most core properties and methods:

class Response:
    charset = 'utf-8'
    default_status = 200
    default_mimetype = 'text/html'
    def __init__(self, response=None, status=None, headers=None,
                 mimetype=None, content_type=None, direct_passthrough=False):
        pass
    @classmethod
    def force_type(cls, response, environ=None):
        pass

It should be noted here that when you look at the Flask source code, what you see is inconsistent with the above code. Response in Flask is actually very concise, because the core function of Response is implemented by Response in Werkzeug framework. The Response class in Werkzeug inherits from the BaseResponse class,
The properties and methods above are defined by BaseResponse.

The three class attributes: charset, default_status and default_mimetype define some default values. If your application configuration does not match the default values, you can customize the properties by inheriting the Response class, so as to avoid modifying these values every time you respond. For example, if your application is an API and all routes return XML, you can modify the default_mimetype to application/xml in your custom response class, so that Flask will return a response in XML format by default.

I will not introduce the init construction method in detail, but it should be noted that the Response construction method receives three important parameters, the response body, the status code and the header. In subclasses, constructors can alter this rule to create specific responses.

The force_type() method plays a very important role in the response class. In some cases, Werkzeug or Flask needs to create its own response object, such as when an error occurs in the application, an error response needs to be returned to the client. In this case, the response object is created by the framework, not generated by the application. When you create a specific response type in an application, Flask and Werkzeug don’t know the details of this specific response type, so the framework still creates standard response types by default. The force_type() method in the response class is responsible for converting different response instances into the form you need.

I believe you must be confused about the role of force_type(). In fact, using this method will transform the response object whenever Flask encounters a response object that does not match the expected class. In the third example below, I’ll show how to use this method to make Flask’s routing function return a response that supports a specific object such as a dictionary, list, or any other form for each request.
The above has described enough principles, and in the following chapters I will implement these programming skills with code.

Use a custom Response class

 Configuring a custom response class for a Flask application is very simple. Let's look at the following example:
from flask import Flask, Response
class MyResponse(Response):
    pass
app = Flask(__name__)
app.response_class = MyResponse
# …

Here I define a response class named MyResponse myself. Usually when we define our own response class, we only need to customize a subclass of Flask’s Response class, and add or modify some behaviors on this basis. After specifying the app.response_class attribute as a custom class, we can make Flask use our own response type.

The attribute response_class of the Flask class is a class attribute, so as a variation on the example above, you can create a subclass of Flask to use your own response class:

from flask import Flask, Response
class MyResponse(Response):
    pass
class MyFlask(Flask)
    response_class = MyResponse
app = MyFlask(__name__)
Example #1: Changing the default value of Response

The first example is very simple. For example, your application returns XML on all endpoints. In this case, we only need to set the default content type to application/xml. This can be done with just two lines of code.

class MyResponse(Response):
    default_mimetype = 'application/xml'

Simple, right? If you set this class as your application’s default response, you can return XML in your routing functions without setting
content type. For example:

@app.route('/data')
def get_data():
    return '''<?xml version="1.0" encoding="UTF-8"?>
<person>
    <name>John Smith</name>
</person>
'''

If the default response class is used, the route will accept a content type of application/xml format by default. if you need different
content type, you only need to override the default value in the return value of the normal routing function,

@app.route('/')
def index():
    return '<h1>Hello, World!</h1>', {<!-- -->'Content-Type': 'text/html'}
Example #2: Automatically determine Content Type

The example below is a bit more complicated. Assuming that the number of HTML and XML routes in this application is equal, it is very inefficient to use the first method at this time, because no matter how you set the default format, half of the routes need to override the default content type.
A better solution is: create a response type that can decide to use content type based on the content of the response. An example is given below:

class MyResponse(Response):
    def __init__(self, response, **kwargs):
        if 'mimetype' not in kwargs and 'contenttype' not in kwargs:
            if response.startswith('<?xml'):
                kwargs['mimetype'] = 'application/xml'
        return super(MyResponse, self).__init__(response, **kwargs)

In this example, the content type is not specified in the initial response. Next, if the response text is
, which indicates that the data is encoded in XML document format. If the above two conditions are true, set the XML content type to
The form of the dictionary is sent as a parameter to the constructor of the parent class.
In this response class, any document in XML format will automatically receive the XML content type, while other responses will continue to use the default content type. And in all classes, we can still automatically specify the content type we need.

Example #3: Automatically set JSON response

The final example will handle a very troublesome situation in Flask. It is a very common situation to use Flask's API to return JSON format, which requires you to call the jsonify() function to convert the Python dictionary into JSON format, and set the content type to JSON in the response. The following is an example of routing processing:

@app.route('/data')
def get_data():
    return jsonify({<!-- -->'foo': 'bar'})

Unfortunately, all routing functions that need to return JSON need to do this, which leads to repeated calls to the jsonify() function at the end of a large number of APIs. From a code readability point of view, is there an alternative?

@app.route('/data')
def get_data():
    return {<!-- -->'foo': 'bar'}

A response class is defined here, which can support the above syntax, so that all routing functions do not need to return JSON:

class MyResponse(Response):
    @classmethod
    def force_type(cls, rv, environ=None):
        if isinstance(rv, dict):
            rv = jsonify(rv)
        return super(MyResponse, cls).force_type(rv, environ)

The routing handler function in Flask can only handle a limited number of types. These types include str, unicode, bytes, bytearray
But when you return some unsupported type, such as the dictionary in the above example, how does Flask handle it? If the returned response type does not match the expected type, Flask considers it an unknown response type, and does not create a response object to return information, but calls the class method force_type() to cast the unknown type. In this example, the response class defined by the subclass overrides and overrides this method, automatically converting the case where the return value is a dictionary. The conversion is achieved by calling the jsonify() method before calling the superclass method.
The trick above will not affect the functionality of other normal response functions. This is because the child class does nothing for any class that returns a normal response, and all requests are passed transparently to the parent class.

https://blog.miguelgrinberg.com/post/customizing-the-flask-response-class

syntaxbug.com © 2021 All Rights Reserved.