flask REST API.

http://flask-restful.readthedocs.io/en/0.3.5/quickstart.html#full-example 

Flask-RESTful provides a Resource base class that can define one or more HTTP methods for a given URL.

The add_resource function registers a route to the framework using the specified endpoint. If no endpoint is specified, Flask-RESTful will generate one based on the class name, but sometimes some functions such as url_for require an endpoint, so I will explicitly assign a value to the endpoint.

class TaskListAPI(Resource):
    def get(self):
        pass

    def post(self):
        pass

class TaskAPI(Resource):
    def get(self, id):
        pass

    def put(self, id):
        pass

    def delete(self, id):
        pass

api.add_resource(TaskListAPI, '/todo/api/v1.0/tasks', endpoint = 'tasks')
api.add_resource(TaskAPI, '/todo/api/v1.0/tasks/<int:id>', endpoint = 'task')

Flask-RESTful provides a better way to handle data validation called the RequestParser class. This class works like the command line parsing tool argparse.

First, for each resource you need to define parameters and how to validate them:

from flask.ext.restful import reqparse

class TaskListAPI(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument('title', type = str, required = True,
            help = 'No task title provided', location = 'json')
        self.reqparse.add_argument('description', type = str, default = "", location = 'json')
        super(TaskListAPI, self).__init__()

    # ...

class TaskAPI(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument('title', type = str, location = 'json')
        self.reqparse.add_argument('description', type = str, location = 'json')
        self.reqparse.add_argument('done', type = bool, location = 'json')
        super(TaskAPI, self).__init__()

In the TaskListAPI resource, the POST method is the only one that accepts parameters. The parameter "title" is required, so I define an error message that "title" is missing. When the client lacks this parameter, Flask-RESTful will send this error message to the client as a response. The "description" field is optional, and when this field is missing, the default empty string will be used. An interesting aspect is that the RequestParser class looks for parameters in request.values ​​by default, so the optional location parameter must be set to indicate that the requested parameters are in request.json format.

Parameter handling for TaskAPI resources works the same way, but with a few differences. The PUT method requires parsing parameters, and all parameters of this method are optional.

When the request parser is initialized, parsing and validating a request is easy. For example, notice how simple the TaskAPI.put() method has become:

def put(self, id):
    task = filter(lambda t: t['id'] == id, tasks)
    if len(task) == 0:
        abort(404)
    task = task[0]
    args = self.reqparse.parse_args()
    for k, v in args.iteritems():
        if v != None:
            task[k] = v
    return jsonify( { 'task': make_public_task(task) } )

Another benefit of using Flask-RESTful to handle validation is that there is no need to handle errors like HTTP 400 separately, Flask-RESTful will handle those.

generate response

The original design of the REST server used Flask's jsonify function to generate the response. Flask-RESTful will automatically handle the conversion to JSON data format, so the following code needs to be replaced:

return jsonify( { 'task': make_public_task(task) } )

Now it needs to be written like this:

return { 'task': make_public_task(task) }

Flask-RESTful also supports custom status codes, if necessary:

return { 'task': make_public_task(task) }, 201

Flask-RESTful has many more features. make_public_task can wrap a task from the origin server from its internal form into the external form the client wants. The most typical is to convert the id of the task into a uri. Flask-RESTful provides a helper function that can do this kind of conversion very elegantly, not only can convert id to uri but also convert other parameters:

from flask.ext.restful import fields, marshal

task_fields = {
    'title': fields.String,
    'description': fields.String,
    'done': fields.Boolean,
    'uri': fields.Url('task')
}

class TaskAPI(Resource):
    # ...

    def put(self, id):
        # ...
        return { 'task': marshal(task, task_fields) }

The task_fields structure is used as a template for the marshal function. fields.Uri is a specific parameter used to generate a URL. The parameter it takes is endpoint.

from flask.ext.restful import reqparse

class TaskListAPI(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument('title', type = str, required = True,
            help = 'No task title provided', location = 'json')
        self.reqparse.add_argument('description', type = str, default = "", location = 'json')
        super(TaskListAPI, self).__init__()

    # ...

class TaskAPI(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument('title', type = str, location = 'json')
        self.reqparse.add_argument('description', type = str, location = 'json')
        self.reqparse.add_argument('done', type = bool, location = 'json')
        super(TaskAPI, self).__init__()

    # ...

 

Official todo code 

from flask import Flask
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)

TODOS = {
    'todo1': {'task': 'build an API'},
    'todo2': {'task': '?????'},
    'todo3': {'task': 'profit!'},
}


def abort_if_todo_doesnt_exist(todo_id):
    if todo_id not in TODOS:
        abort(404, message="Todo {} doesn't exist".format(todo_id))

parser = reqparse.RequestParser()
parser.add_argument('task')


# Todo
# shows a single todo item and lets you delete a todo item
class Todo(Resource):
    def get(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        return TODOS[todo_id]

    def delete(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        del TODOS[todo_id]
        return '', 204

    def put(self, todo_id):
        args = parser.parse_args()
        task = {'task': args['task']}
        TODOS[todo_id] = task
        return task, 201


# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):
    def get(self):
        return TODOS

    def post(self):
        args = parser.parse_args()
        todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
        todo_id = 'todo%i' % todo_id
        TODOS[todo_id] = {'task': args['task']}
        return TODOS[todo_id], 201

##
## Actually setup the Api resource routing here
##
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')


if __name__ == '__main__':
    app.run(debug=True)

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325185572&siteId=291194637