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]

  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)
  2. 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)
  3. The server must inform the client whether or not the requests can be cached. (Cacheable Rule)
  4. 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)
  5. A uniform method of communication must exist between a client and a server. (Uniform Interface Rule)
  6. 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

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
from 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

edit

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

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

  1. 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: