Skip to content

Latest commit

 

History

History
230 lines (183 loc) · 7.45 KB

13. REST.md

File metadata and controls

230 lines (183 loc) · 7.45 KB

Introduzione

REST - REpresentational State Transfer - è uno stile architetturale per servizi web. Enfatizza semplicità e libertà di personalizzazione, ponendo attenzione a:

  • uniformità delle interfacce
  • scalabilità
  • indipendenza dei componenti

#Nota spesso si ci riferisce a API HTTP identificandole come RESTful. Questo è un uso improprio del termine.

Idea: un server risponderà a richieste da parte del client con oggetti in formato HTML, JSON, XTML, etc. contenenti hyperlink che possono essere seguiti dal client per cambiare lo stato del sistema. Solo il primo identificatore deve essere conosciuto a priori, il resto è scoperto: loose coupling tra client e server; agilità nel cambiamento dei link.

Limitazioni di HTTP

HTTP è stateless by-design. Robusto contro interruzioni di comunicazione.

REST permette di costruire web API. Gli endpoint che seguono REST hanno le seguenti caratteristiche:

  • stateless
  • supporta verbi HTTP
  • restituisce dati in formato JSON, XML, HTML, etc.

Django REST framework

Setup

L'applicazione REST framework permette di integrare al framework tradizionale la gestione di web API.

Installabile con:

pip3 install djangorestframework

Poi da aggiungere alle applicazioni installate:

INSTALLED_APPS = [
	'rest_framework',
	...
]

Questa app si occupa di serializzare i dati e servirli mediante gli URL.

Serializer

L'oggetto Serializer permette di convertire un oggetto complesso (eg. appartenente a Model) in formato json

Esempio di serializzazione di un libro Book:

from rest_framework import serializers, generics
from books.models import Book

class BookSerializer(serializers.ModelSerializer):
	class Meta:
		model = Book
		fields = ('id', 'title', 'description')

class BookListAPIView(generics.ListAPIView):
	queryset = Book.objects.all()
	serializer_class = BookSerializer

CORS

Progettare API REST permette di disaccoppiare (rendere indipendenti) frontend e backend.

Come fa il frontend ad avere accesso ai dati serviti dal backend?

Il meccanismo CORS - Cross-Origin Resource Sharing - è un meccanismo per l'accesso a risorse servite da un server su un dominio differente (eg. immagini, stylesheets, iframes, videos, etc.). Client e server interagiscono per determinare se sia sicuro permettere la cross-origin request.

Installabile con pip install django-cors-headers. Aggiunta tra le app installate:

INSTALLED_APPS = [
  'corsheaders',
  ...
]

Aggiunta del middleware corsheaders.middleware.CorsMiddleware. Nota che deve essere inserito PRIMA di commonMiddleware.

L'accesso viene quindi permesso ai domini localhost:8000 e localhost:3000:

CORS_ORIGIN_WHITELIST = [
 'http://localhost:8000',
 'http://localhost:3000'
]

REST Views

Tra i tipi di API che si possono utilizzare esistono:

  • ListAPIView
  • CrateAPIView
  • RetrieveAPIView
  • UpdateAPIView
  • DestroyAPIView

Le prime 2 hanno bisogno di specificare l'ID dell'oggetto nell'header.

Viewsets

Evitano la scrittura di codice boilerplate, in particolare la riscrittura di tutte le 5 views per ogni oggetto. Django REST framework mette a disposizione i viewsets, ovvero classi che integrano tutte le funzioni CRUD.

from rest_framework import viewsets

class BookViewSet(viewsets.ModelViewSet):
	queryset = Book.objects.all()
	serializer_class = BookSerializer

Routers

Per risparmiare il codice degli url:

from rest_framework import routers

router = SimpleRouter()
router.register('books', BookViewSet, basename='books')
urlpatterns = router.urls

Gestione dei permessi

I permessi possono essere impostati a livello di:

  • Project: policy di accesso applicate globalmente, su tutto il progetto
  • App: policy di accesso appliate a tutti gli endpoint di una certa APP
  • View: policy di accesso applicate a una singola API

Permessi sul progetto

REST_FRAMEWORK = {
  'DEFAULT_PERMISSION_CLASSSES': ['rest_framework.permissions.IsAuthenticated']
}

IsAuthenticated si contrappone a AllowAny.

Permessi sulle view

I permessi su una singola view si definiscono mediante l'attributo permission_classes: Tuple[permissions.BasePermission] di una APIView.

Permessi personalizzati

Tipicamente nel file permisssions.py si inseriscono classi di permesso come:

from rest_framework import permissions

class IsAuthorOrReadOnly(permissions.BasePermission):
	def has_object_permissions(self, request, view, obj):
		if request.method in permissions.SAFE_METHODS:
			return True
	return obj.created_by == request.user

Autenticazione

Data la natura stateless del protocollo HTTP, esso non fornisce la logica per l'autenticazione.

Esistono 3 tipi di autenticazione:

  • Base
  • Di sessione
  • Con token

Autenticazione HTTP base

  1. Request from c to s
  2. S responds with 401 Unauthorized. Invia header WWW-Authenticate
  3. C invia le credenziali contenute in Authorization
  4. S risponde con 200 OK o 403 Forbidden

Da questo momento in poi il client invia le future richieste usando le credenziali di Authorization header.

Pro: standardizzato e di semplice implementazione Contro: autenticazione ad ogni richeista. Credenziali in chiaro (codificate in b64), quindi da usare solo se abbinata a HTTPS.

Autenticazione di sessione

Per arginare le limitazioni della statelessness di HTTP, si introducono due strumenti per memorizzare variabili lato client: sessioni e cookies.

  1. C si autentica
  2. S rilascia cookie, identificativo della sessione
  3. Il cookie di sessione sarà inserito in ogni richiesta HTTP futura
  4. L'ID di sessione viene distrutto al logout

#Nota: questo approccio è stateful, poichè viene mantenuto lo stesso stato sia sul server che sul client

Autenticazione con token

Autenticazione stateless che prevede la memorizzazione di un token univoco lato client.

  1. Utente accede dal C
  2. C invia le credenziali a S
  3. S verifica e in caso di successo genera un token
  4. Il token viene salvato nel local storage di C
  5. Il token viene passato nell'header di ogni richiesta HTTP

Il controllo di validità viene fatto sul token, senza essere a conoscenza delle informazioni sull'utente.

Pro: scalabile e multipiattaforma Contro: inefficiente, in quanto le informazioni sul client devono essere inviate ad ogni richiesta.

Su Django è supporata da TokenAuthentication. L'implementazione è minimale.

#Vedi: JWT - JSON Web Token

Implementazione

REST_FRAMEWORK = {
  'DEFAULT_PERMISSION_CLASSES': [...],
  'DEFAULT_AUTHENTICATION_CLASSES': [
	  'rest_framework.authentication.SesssionAuthentication',
	  'rest_framework.authentication.TokenAuthentication'
  ] 
}

Aggungi l'app rest_framework.authtoken e migra.

Per l'autenticazione si usa django-rest-auth che espone dei suoi endpoint di autenticazione, ad esempio:

urlpatterns = [
   path('api/v1/dj-rest-auth', include('dj_rest_auth.urls'))
]

Registrazione utente

Per la registrazione dell'utente si usa l'applicazione django-allauth. Poi aggiungi tra le app installate:

INSTALLED_APPS = [
  ...,
  'django.contrib.sites',
  'allauth',
  'allauth.account',
  'allauth.socialaccount',
  'dj_rest_auth.registration'
]

Per la registrazione dell'utente aggiungere l'endpoint:

urlpatterns = [
   ...,
   path('api/v1/dj-rest-auth/registration/', include('dj_rest_auth.registration.urls'))
]

Per evitare la configurazione di un server mail si può definire email backend:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
SITE_ID = 1