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)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.
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
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
No comments:
Post a Comment