Web Technologies/2021-2022/Laboratory 12
REST (REpresentational State Transfer) is emerging as the standard architectural design for w:web APIs and services.
Here we learn how to use Python and the Flask microframework to create a web service that is "RESTful".[1]
- There must be a separation between the server and the client, i.e., the one who offers and the one who consumes a service. (Client-server rule)
- Requests from a client must contain all the information needed by the server: the server does not store information provided by the client in one request, and then use it in a different request. In other words, the first request does not alter the server's "state" in a way that modifies the response to another request. (Stateless Rule)
- The server must inform the client whether or not the requests can be cached. (Cacheable Rule)
- All communications between a client and a server must be standardized in the following way: Intermediaries must be able to respond to requests instead of the end server in a fashion that does not require additional steps on the part of the client. (Layered System Rule)
- A uniform method of communication must exist between a client and a server. (Uniform Interface Rule)
- The following rule is optional: Servers should be able to provide executable code (scripts) for clients to execute in their context. (Code on Demand Rule)
For more discussion of these rules visit blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask
Defining a RESTful web service
[edit | edit source]The REST architecture was originally designed to fit the HTTP protocol used by the world wide web.
A Uniform Resource Identifier (URI) is a character sequence that identifies a logical (abstract) or physical resource. The clients send requests to URIs using the methods defined by the HTTP protocol. Possibly as a result of that request, the state of the affected resource will change. This process is central to the concept of RESTful web services.
These HTTP request methods will affect a given resource in standard ways.
Using Flask route restrictions for HTTP verbs (GET, POST, PUT, PATCH and DELETE) while defining the routes to interact with a model is one way of implementing REST.
Minimal example using Flask: and Flask-SQLAlchemy
[edit | edit source]from flask import Flask, request, json
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
# Resource model
class Resource(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), nullable=False)
def __repr__(self):
return str(self.id) + ',' + self.name
def __str__(self):
return self.__repr__()
def create_db():
db.create_all()
r1 = Resource(name="Resource 1")
r2 = Resource(name="Resource 2")
r3 = Resource(name="Resource 3")
db.session.add(r1)
db.session.add(r2)
db.session.add(r3)
db.session.commit()
# create_db()
@app.route('/')
def hello_world():
return 'Hello, World!'
@app.route('/api/resource/<int:res_id>', methods=['GET'])
def get_resource(res_id):
res = Resource.query.filter_by(id=res_id).first()
if res:
id, name = str(res).split(',')
return app.response_class(json.dumps({'id': id, 'name': name}), status=200, mimetype='application/json')
else:
return app.response_class(status=404)
@app.route('/api/resource', methods=['POST'])
def add_resource(newres=None):
post_body = request.json
new_res = Resource(name=post_body['name'])
db.session.add(new_res)
db.session.commit()
return app.response_class(json.dumps(post_body), status=201, mimetype='application/json')
@app.route('/api/resource/<int:res_id>', methods=['DELETE'])
def delete_resource(res_id):
res = Resource.query.filter_by(id=res_id).delete()
db.session.commit()
return app.response_class(status=202, mimetype='application/json')
@app.route('/api/resource/<int:res_id>', methods=['PUT'])
def replace_resource(res_id):
put_body = request.json
delete_resource(res_id)
add_resource(post_body)
return app.response_class(status=202, mimetype='application/json')
@app.route('/api/resources', methods=['GET'])
def get_resources():
resources = Resource.query.all()
res = {'resources': []}
for resource in resources:
id, name = str(resource).split(',')
res['resources'].append({'id': id, 'name': name})
if resources:
return res
else:
return app.response_class(status=404)
Exercises
[edit | edit source]Create a REST API that has the role of an artefact repository. An artefact is defined as any file which can be stored onto a disk (i.e. image, document, source code etc.). The REST API should be able to:
- Have a directory structure which contains the artefacts:
- Each artifact could be stored in a directory which indicates its purpose (you can think of it as grouping artefacts of a project)
- It should have a resources that allows the listing of all directories and the artefacts currently contained in a directory
- Resource for fetching, adding and deleting artefacts
Examples for file uploads using Flask can be found here and here.
Example
[edit | edit source]Here is an example structure of the REST API:
GET /artefacts/
- returns list of available directories
GET /artefacts/<directory>
- returns list of all artefacts from directory
POST /artefacts/<directory>/<artefact_id>
- push artefact in repository
GET /artefact/<directory>/<artefact_id>
- get artefact by id from directory
DELETE /artefact/<directory>
- delete entire directory and artefacts
DELTE /artefact/<directory>/<artefact_id>
- delete artefact
PUT /artefact/<directory>/<artefact_id>
- replace artefact (deleting the old one)
After you have finished consider how you can add support for more advanced features such as versioning of artifacts.
Resources:
https://flask-restful.readthedocs.io/en/latest/
Alexandru Munteanu, 2021-12-11, alexandru.munteanu@e-uvt.ro
- ↑ https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask. In particular, six design rules have been proposed for a REST system: