gogoWebsite

Unified interface return format under flask-restplus

Updated to 15 days ago

background

When using flask+flask-restplus, the interface function returns an object that can be serialized by json when the business is normal

@ns.route('/hello')
class Hello(Resource):
    def get(self):
        return ['hello', 'hi']

The interface returns the following content:

[
    "hello",
    "hi"
]

When a business exception (such as a parameter error detected), the abort function will usually be called and it will throw an HTTPException

@ns.route('/error')
class Error(Resource):
    def get(self):
        abort(400, 'params error')

The interface returns the following content:

{
    "message": "params error"
}

Due to company specification requirements, a unified http request response template is required, as follows:

{
    'code': 100000,
    'message': 'xxx'
    'data': 'hello'
}

Therefore, we need to modify the interface return format.

Unified processing of interface return value

In order not to change the original encoding habits and to meet the above requirements for return formats, we can use the representation decorator of flask-restplus to register an interface return content processor

@api.representation('application/json')
def output_json(data, code, headers=None):
    result = {}
    if api.specs_url == request.url:
        result = data
    elif code in (200, 201, 204):
        result['code'] = 100000
        result['data'] = data
    else:
        result['code'] = data.get('code') or 200000
        result['message'] = data['message']
    response = make_response(json.dumps(result), code)
    response.headers.extend(headers or {})
    return response

Now let’s request the above two interfaces, and the return format becomes the format we want

{"code": 100000, "data": ["hello", "hi"]}
{"code": 200000, "message": "params error"}

Custom exception handling

If an exception that is not an HTTPException is thrown, you will find that the specific exception content cannot be obtained, and you will only get the following results:

{"code": 200000, "message": "Internal Server Error"}

If you want to return some specified exception content, for example, we have customized the following exception class:

class BaseException(Exception):
    http_code = 500
    business_code = 200000

    def __init__(self, message=None):
        if message:
            self.message = message

    def to_dict(self):
        data = {'code': self.business_code, 'message': self.message}
        return data


class InsufficientPrivilege(BaseException):
    http_code = 403
    business_code = 200001
    message = 'Insufficient permissions'

Then we can use the errorhandler decorator of flask-restplus to register an exception handler, in which we convert the custom exception to the normal interface return format

@api.errorhandler(BaseException)
def handle_base_exception(error):
    return error.to_dict(), error.http_code

This way, custom exceptions can also return exception content

{"code": 200001, "message": "Insufficient permissions"}

Of course, we cannot turn all exceptions into BaseException and throw them all in one go. If we do this, we will also throw exceptions caused by unsolicitation of the program, which will bring bad experience to the user.