Архитектура restful api на pyramid — приемы проектирования...

Post on 16-Apr-2017

630 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Архитектура RESTful API на Pyramid

приёмы проектирования

Типичный проект

API

Mobile AppsAdmin PanelWeb Site

BackgroundWorkers

Data Sources

Типичный проект

API

Mobile AppsAdmin PanelWeb Site

BackgroundWorkers

Data Sources

Слои

Данные

Бизнес логика

Представление

Слои

Данные

Бизнес логика

Представление

Model

Controller

View

Слои

Данные

Бизнес логика

Представление

Model

Controller

View

Store

Resource

View

Resource

Resource

―Унифицированный интерфейс к данным

Resource

―Унифицированный интерфейс к данным

―Унифицированные исключения

Resource

―Унифицированный интерфейс к данным

―Унифицированные исключения

―Независимость от представления

Traversal

Traversal

child = parent['child']assert child.__name__ == 'child'assert child.__parent__ is parent

Traversal

child = parent['child']assert child.__name__ == 'child'assert child.__parent__ is parent

GET /users/1/

root['users']['1']

Traversal

child = parent['child']assert child.__name__ == 'child'assert child.__parent__ is parent

GET /users/1/ -> 404 Not Found

root['users']['1'] -> KeyError

Traversal

class Root(object): """ API root resource """

def __getitem__(self, name): if name == 'users': return Users(name, self) raise KeyError(name)

class Users(object): """ Collection of users """

def __init__(self, name, parent): self.__name__ = name self.__parent__ = parent

TraversalKit

from traversalkit import Resource, DEC_ID

class Root(Resource): # / """ API root resource """

@Root.mount('users') # /users/class Users(Resource): """ Collection of users """

@Users.mount_set(DEC_ID) # /users/{^[0-9]+$}/class User(Resource): """ Particular user """

Коллекции

class Articles(Resource):

def get_list(self, offset=0, limit=10): pass

def get_map(self, ids): pass

def create(self, title, body): pass

Элементы

@Articles.mount_set(DEC_ID)class Article(Resource):

def update(self, title, body): pass

def delete(self): pass

Загрузка элемента

from ..store import db

@Articles.mount_set(DEC_ID)class Article(Resource):

__not_exist__ = db.NoResultFound

def on_init(self, data=None): if data is None: # Created via __getitem__ session = db.Session() data = session \ .query(db.Article) \ .filter(db.Article.id == int(self.__name__)) \ .one() self.data = data

Загрузка списка

from ..store import db

class Articles(Resource):

def get_list(self, offset=0, limit=50): session = db.Session() query = session \ .query(db.Article) \ .limit(limit) \ .offset(offset) \ .order_by(db.Article.created_at)

return [ self.get(str(item.id), item) for item in query.all() ]

Иерархия

@Root.mount('articles') # All articlesclass Articles(Resource): """ Collection of articles """

@Article.mount('comments') # Article's commentsclass Comments(Resource): """ Collection of comments """

Иерархия

@Root.mount('articles') # All articles@User.mount('articles') # User's articlesclass Articles(Resource): """ Collection of articles """

@Article.mount('comments') # Article's comments@User.mount('comments') # User's commentsclass Comments(Resource): """ Collection of comments """

ACL

from pyramid.security import Allow, Everyone, Authenticated

class Articles(Resource):

__acl__ = [ (Allow, Everyone, 'read'), (Allow, Authenticated, 'create'), (Allow, 'group:moderators', ('update', 'delete')), ]

@Articles.mount_set(DEC_ID)class Article(Resource):

def __acl__(self): user_id = str(self.data.user_id) return [(Allow, user_id, ('update', 'delete'))]

ACL

from pyramid.security import \ Allow, Everyone, Authenticated, DENY_ALL

@Article.mount('comments')class Comments(Resource):

__acl__ = [ (Allow, Everyone, 'read'), (Allow, Authenticated, 'create'), (Allow, 'group:moderators', ('update', 'delete')), DENY_ALL, # Stop inheritance ]

Валидация

from .. import validation

class Articles(Resource, validation.Mixin):

@validation.set_schema(validation.schema.Article) def create(self, title, body): pass

def create_article(context, request): article = context.validated.create(request.json)

Views

from pyramid.view import view_config

from ..resources import Articles

@view_config(context=Articles, request_method='GET', permission='read')def list_articles(context, request): articles = context.validated.get_list(request.GET) return { 'articles': articles, }

Views

from pyramid.view import view_config

from ..resources import Articles

@view_config(context=Articles, request_method='GET', permission='read')def list_articles(context, request): articles = context.validated.get_list(request.GET) users = request.root['users'].get_map( set(a.data.user_id for a in articles) ) return { 'articles': articles, 'users': users, }

Сериализация

class Article(Resource):

def __json__(self, *_): return { 'id': self.data.id, 'title': self.data.title, 'body': self.data.body, }

Обработка ошибок

from pyramid.view import view_configfrom pyramid.security import NO_PERMISSION_REQUIRED

from ..resources import ResourceExceptionfrom ..validation import Invalid

@view_config(context=ResourceException, permission=NO_PERMISSION_REQUIRED)def resource_exception(context, request): request.response.status_int = 400 return {'error': context}

@view_config(context=Invalid, permission=NO_PERMISSION_REQUIRED)def validation_exception(context, request): request.response.status_int = 422 return {'error': context}

Обработка ошибок

400422

Ошибка бизнес логикиНевалидные данные

Обработка ошибок

400422403404

Ошибка бизнес логикиНевалидные данныеНет прав доступаРесурса не существует

Обработка ошибок

400422403404405

Ошибка бизнес логикиНевалидные данныеНет прав доступаРесурса не существуетОшибка контекста

Обработка ошибок

400422403404405500

Ошибка бизнес логикиНевалидные данныеНет прав доступаРесурса не существуетОшибка контекстаВсё сломалось

Тесты

ResourceViewWSGI

Тесты

ResourceViewWSGI

Бизнес логика, иерархия

Тесты

ResourceViewWSGI

Бизнес логика, иерархияВалидация, связанные данные, сессия

Тесты

ResourceViewWSGI

Бизнес логика, иерархияВалидация, связанные данные, сессияИнтеграция, ACL

Вопросы?

top related