In Previous article, I created a concept app for microservices using flask, cerberus, and peewee. This time, I will explain how to check Content-Type by flask, which was omitted at that time. I would also like to introduce how to create a function decorator that checks the Content-Type.
@content_type
function decorator introduced this timeroute ()
function Grant to a function that grants a decorator (ie REST API)@content_Example of using the type function decorator
# rest api
@app.route('/questions', methods=['POST'])
@content_type('application/json')
def register_question():
In flask, you can refer to the HTTP request header information with request.headers
.
request.headers
is a dictionary-type object whose key is the header name.
So, when checking Content-Type, get the value using Content-Type as a key.
See http://flask.pocoo.org/docs/0.12/api/#incoming-request-data for more information.
Poor sample code
# -*- coding: utf-8 -*-
import os
from flask import Flask, abort, request, make_response, jsonify
# flask
app = Flask(__name__)
# rest api
@app.route('/questions', methods=['POST'])
def register_question():
#Content type (application/json) check
if not request.headers.get("Content-Type") == 'application/json':
error_message = {
'error':'not supported Content-Type'
}
return make_response(jsonify(error_message), 400)
# omitted :Execution of business logic
#Return of processing result (JSON format)
return make_response(jsonify({'result': True}))
@app.route('/questions/<string:question_code>', methods=['PUT'])
def update_question(question_code):
#Content type (application/json) check
if not request.headers.get("Content-Type") == 'application/json':
error_message = {
'error':'not supported Content-Type'
}
return make_response(jsonify(error_message), 400)
# omitted :Execution of business logic
#Return of processing result (JSON format)
return make_response(jsonify({'result': True}))
# main
if __name__ == "__main__":
app.run(host=os.getenv("APP_ADDRESS", 'localhost'), \
port=os.getenv("APP_PORT", 3000))
Since there are many places to check the Content-Type, the problem of Code Clone / Duplicate Code will occur. It can be defined as a utility function, but since it does not meet the prerequisites before calling the API, it is just a personal impression, but I did not want to describe the process of calling the utility.
A function decorator can only call the target function (here REST API) if it passes the check, that is, if the prerequisites are met. As a result, I decided to implement the function decorator shown below.
@content_type
function decorator for a modern lookcontent_type_check_app.py
# -*- coding: utf-8 -*-
import os
from flask import Flask, abort, request, make_response, jsonify
import functools
# flask
app = Flask(__name__)
# check content-type decorator
def content_type(value):
def _content_type(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
if not request.headers.get("Content-Type") == value:
error_message = {
'error': 'not supported Content-Type'
}
return make_response(jsonify(error_message), 400)
return func(*args,**kwargs)
return wrapper
return _content_type
# rest api
@app.route('/questions', methods=['POST'])
@content_type('application/json')
def register_question():
# omitted :Execution of business logic
#Return of processing result (JSON format)
return make_response(jsonify({'result': True}))
@app.route('/questions/<string:question_code>', methods=['PUT'])
@content_type('application/json')
def update_question(question_code):
# omitted :Execution of business logic
#Return of processing result (JSON format)
return make_response(jsonify({'result': True}))
# main
if __name__ == "__main__":
app.run(host=os.getenv("APP_ADDRESS", 'localhost'), \
port=os.getenv("APP_PORT", 3000))
@content_type
You can now just add @ content_type ('application / json')
to your REST API functions.
You can intuitively see that the Content-Type should be application / json without the code cloning problem.
(If necessary, make application / json constant)
** By the way, if you change the description order of @ app.route
and @ content_type
, an error will occur. ** **
Python's function decorators are nesting of functions rather than landmarks like Java annotations, so the order of nesting affects the processing flow.
This is because the function of flask is called in the processing of @content_type
, so it is necessary to execute flask first as a nest.
democlient.py
# -*- coding: utf-8 -*-
import requests
import json
def register_question_none_content_type(url, data):
print("[POST] /questions : none content-type")
response = requests.post(url, json.dumps(question))
print(response.status_code)
print(response.content)
def register_question(url, data):
print("[POST] /questions : content-type application/json")
response = requests.post(url, json.dumps(question), \
headers={'Content-type': 'application/json'})
print(response.status_code)
print(response.content)
# main
if __name__ == "__main__":
url = 'http://localhost:3000/questions'
question = {
'question_code' : '9999999999',
'category' : 'demo',
'message' : 'hello'
}
# Content-No Type
register_question_none_content_type(url, question)
# Content-Type : application/json
register_question(url, question)
As described in Previous article, if you specify data in the json argument like json = data
, requests will automatically apply / Set the Content-Type of json.
Since this function is not used this time, the direct transmission data (json.dumps (question)
) is set in the second argument.
Server startup (and execution log)
C:\tmp\>python content_type_check_app.py
* Running on http://localhost:3000/ (Press CTRL+C to quit)
127.0.0.1 - - [16/Aug/2017 20:02:30] "POST /questions HTTP/1.1" 400 -
127.0.0.1 - - [16/Aug/2017 20:02:30] "POST /questions HTTP/1.1" 200 -
The first line of the execution log is an HTTP request for which Content-Type is not specified, and the HTTP status is 400. The second line is the specified HTTP request. In this case, the process is executed normally and 200 is returned.
Client execution
C:\tmp\python democlient.py
[POST] /questions : none content-type
400
{
"error": "not supported Content-Type"
}
[POST] /questions : content-type application/json
200
{
"result": true
}
We have confirmed that HTTP requests that do not specify Content-Type will return an HTTP status of 400 error and the error content {'error':'not supported Content-Type'}
as designed.
Since the next request specifies application / json for Content-Type, it has passed the Content-Type check and the correct processing result with HTTP status of 200 is returned.
This time I explained how to check Content-Type in flask.
If ** flask can perform request mapping even with Content-Type, Content-Type will be guaranteed when REST API is called **, so the @content_type
created this time will not be necessary.
Also, I created a function decorator for the first time this time, but since I can add functions later like AOP, I felt that it could be used in various other ways.
Recommended Posts