Flask-RESTful

A cleaner and easier way to implement RESTful api

  This site provide a pretty good explanation of Flask-RESTful;
Flask-RESTful is an extension for Flask that adds support for quickly building REST APIs. It is a lightweight abstraction that works with your existing ORM/libraries. Flask-RESTful encourages best practices with minimal setup. If you are familiar with Flask, Flask-RESTful should be easy to pick up.
Let's see how we can use this package to improve our code in previous section.

1. Install flask-restful package

It's didn't come with normal flask package. So, you have to install it.


pip3 install flask-restful

2. RESTful  API Resource Method Chart

Well, this is nothing to do with the the real implementation, but this chart will act as an design document. It can also be used as an reference document when user going to use this API.

Resource Method usedfor Param OnError
hello_world
GET
get "Hello World" string"
None
200 OK
addTwoNum
POST
add 2 number
and return output
a:int
b:int
200 OK
301 Missing Param
sumAll
POST
add up all number
in array
a:int[]
200 OK
301 Missing Param
302 Param has wrong data type
isPalindrome
POST
check if word or sentence
is a palindrome or not
inString:String
200 OK
301 Missing Param
divideTwoNum
POST
add 2 number
and return output
a:int
b:int
302 Param has wrong data type
301 Missing Param
303 Divider is Zero

3. Import flask restful package

Beside flask, we also need to import flask restful library. From this library, what we need now is API and Resource

from flask import Flask, jsonify, request
from flask_restful import Api, Resource

4. API Initialize

Similar to previous code, we have to initialize our Flask, and now we also need to initialize our RESTful api (or in the code it's just call api). With this new object, the code for attaching our method to the end point(or app.route) will be easier and cleaner. 

#some flask and flask restful initialize process
app = Flask(__name__)
api = Api(app)

5. Implement Resource Method

Now we going to implement the thing that we have designed in the table above. For this particular implementation, my response's json file will have a format as described below;


==== a json format ====
{ 
  "return":retVal,
  "StatusCode": statusValue
}

  • retVal will return the value suitable for each resource or else it going to return a string "Error Message"
  • statusValue will return interger value of OnError code

And since it will always have this format, we going to implement a function to create this format and our resources will call upon them. (we wouldn't want to have duplicate implementation right?)

#constant value (not really have in python)
retKey = ["return","statusCode"]
statusCode = {"OK":200,"MissingParam":301, "wrongDataType"}

#some common function
def errorResponse(errorKey):
    dictData = {
            retKey[0]: "Error Message",
            retKey[1]: statusCode[errorKey]
            }
    return jsonify(dictData)

def passResponse(retVal):
    dictData = {
            retKey[0]: retVal,
            retKey[1]: statusCode["OK"]
            }
    return jsonify(dictData)

then here come the re-implementation of hello world

class hello_world(Resource):
    def get(self):
        #resource hello_world is requested using method GET
        return passResponse("hello world")

for those of you who never use the concept of OOP in python when the class has a input parameter, it's actually mean this class is inherited from that parameter (or actually it's a class)
translate that into English sentence we will have;
class hello is a Resource
and because of this inheritance, we can use all the field and method that is implemented in Class Resource
next Resource is addTwoNumber. Well it's quite similar to the previous one but, since we have an input from our client, we then need to check  it is in correct format for our resource or not.


class addTwoNumber(Resource):
    def post(self):
        #resource addTwoNumber is requested using method POST

        #Get posted data:
        postedData = request.get_json()

        #check for input parameters validity
        if "a" not in postedData or "b" not in postedData:
            return errorResponse("MissingParam")
        
        #calculate and return response
        y = postedData["a"] + postedData["b"]
        return passResponse(y)

now you should understand how we can implement the resource. Let's skip other resources and go to the next step. (all the code and resources will be posted at the end of this blog)

6. Mapping Resource Method with Endpoint

well it's quite straightforward let's see the code.

api.add_resource(hello_world, "/")
api.add_resource(addTwoNumber, "/addTwoNumber")
api.add_resource(sumAll, "/sumAll")

and the last thing to do is the same we need to have the part that run the app.


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

7. Test it out

Yep just run it and test it out with postman


below is the whole code;


from flask import Flask, jsonify, request
from flask_restful import Api, Resource

#some flask and flask restful initialize process
app = Flask(__name__)
api = Api(app)

#constant value (not really have in python)
retKey = ["return","statusCode"]
statusCode = {"OK":200,"MissingParam":301, "wrongDataType":302, "DividerZero":303}

#some common function
def errorResponse(errorKey):
    dictData = {
            retKey[0]: "Error Message",
            retKey[1]: statusCode[errorKey]
            }
    return jsonify(dictData)

def passResponse(retVal):
    dictData = {
            retKey[0]: retVal,
            retKey[1]: statusCode["OK"]
            }
    return jsonify(dictData)

#addition function
def checkPalindrome(inStr):
    head = 0
    tail = len(inStr) -1
    isPalin = True
    while(head<tail):
        if inStr[head] == " ":
            head += 1
            continue
        if inStr[tail] == " ":
            tail -= 1
            continue
        if inStr[head] != inStr[tail]:
            isPalin = False
            break
        head += 1
        tail -= 1
        
    return isPalin
#all the resource

class hello_world(Resource):
    def get(self):
        #resource hello_world is requested using method GET
        return passResponse("hello world")

class addTwoNumber(Resource):
    def post(self):
        #resource addTwoNumber is requested using method POST

        #Get posted data:
        postedData = request.get_json()

        #check for input parameters validity
        if "a" not in postedData or "b" not in postedData:
            return errorResponse("MissingParam")
        
        #calculate and return response
        y = postedData["a"] + postedData["b"]
        return passResponse(y)

class sumAll(Resource):
    def post(self):
        postedData = request.get_json()
        if "a" not in postedData:
            return errorResponse("MissingParam")
        
        if type(postedData["a"]) != list:
            return errorResponse("wrongDataType")

        y = sum(postedData["a"])
        return passResponse(y)

class isPalindrome(Resource):
    def post(self):
        postedData = request.get_json()
        if "inString" not in postedData:
            return errorResponse("MissingParam")
        
        if not isinstance(postedData["inString"],str):
            return errorResponse("wrongDataType")
        y = checkPalindrome(postedData["inString"])
        
        return passResponse(y)

class divideTwoNum(Resource):
    def post(self):
        postedData = request.get_json()
        if "a" not in postedData or "b" not in postedData:
            return errorResponse("MissingParam")
        if postedData["b"] == 0:
            return errorResponse("DividerZero")
        
        #calculate and return response
        y = postedData["a"]/postedData["b"]
        return passResponse(y)

api.add_resource(hello_world, "/")
api.add_resource(addTwoNumber, "/addTwoNumber")
api.add_resource(sumAll, "/sumAll")
api.add_resource(isPalindrome, "/isPalindrome")
api.add_resource(divideTwoNum, "/divideTwoNum")

if __name__ =="__main__":
    app.run(host='0.0.0.0',debug=True) 


host='0.0.0.0' to make the server externally visible

Deemarc Burakitbumrung

A software develper who graduated from Electronic and Communication Engineer. Have an interest in the field of programming and embedded system.

No comments:

Post a Comment