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
editThe 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
editfrom flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask import request, json
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///artifacts.db'
db = SQLAlchemy(app)
class Artifact(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(150), nullable=False)
def __str__(self):
return f"{self._id} -> {self._name}"
@app.route('/api/artifact/<int:id>', methods=['GET'])
def get_artifact(id):
artifact = Artifact.query.filter_by(id=id).first()
if artifact:
return_obj = {'id': artifact.id,
'name': artifact.name}
return app.response_class(json.dumps(return_obj), status=200, mimetype='application/json')
else:
return app.response_class("404 resource not found", status=404)
@app.route('/api/artifact', methods=['POST'])
def add_artifact():
post_body = request.json
new_artifact = Artifact(id=post_body['id'], name=post_body['name'])
# try/catch block in case this cannot be added
try:
db.session.add(new_artifact)
db.session.commit()
return app.response_class(json.dumps(post_body), status=201, mimetype='application/json')
except Exception as e:
return app.response_class('Object already exists!', status=409)
@app.route('/api/artifact/<int:id>', methods=['DELETE'])
def delete_artifact(id):
res = Artifact.query.filter_by(id=id).delete()
db.session.commit()
return app.response_class(status=202, mimetype='application/json')
@app.route('/api/artifact/<int:id>', methods=['PUT'])
def replace_artifact(id):
body = request.json
res = Artifact.query.filter_by(id=id).delete()
new_artifact = Artifact(id=body['id'], name=body['name'])
db.session.add(new_artifact)
db.session.commit()
return app.response_class(status=202, mimetype='application/json')
@app.route('/api/artifacts', methods=['GET'])
def get_artifacts():
# resp = db_conn.execute(f'SELECT * FROM Artifacts')
resources = Artifact.query.all()
if resources:
return_obj = []
for entry in resources:
return_obj.append({'id': entry.id,
'name': entry.name})
return app.response_class(json.dumps(return_obj), status=200, mimetype='application/json')
else:
return app.response_class(status=404)
Exercises
editCreate 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
editHere 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: