Upload
igor-kuznetsov
View
175
Download
0
Embed Size (px)
Citation preview
План
• Что такое API?• С чего начать проектирование?• Какими должны быть формат и структура ответа?
• Валидация больших структур• Обработка исключений
• Версионирование• Как написать клиент к вашему API?• Зачем все это нужно?
2
пятница, 14 июня 13 г.
Что такое API?
3
пятница, 14 июня 13 г.
Что такое API?
Application Programming Interface
3
пятница, 14 июня 13 г.
Что такое API?
Application Programming InterfaceНабор методов, предоставляемых приложением(сервисом, классом, модулем) для использования во внешних программных продуктах
3
пятница, 14 июня 13 г.
Что такое API на самом деле?
4
пятница, 14 июня 13 г.
Интерфейс должен быть
5
пятница, 14 июня 13 г.
Интерфейс должен быть
• Интуитивно понятным
5
пятница, 14 июня 13 г.
Интерфейс должен быть
• Интуитивно понятным• Идеально продуманным с самого
начала
5
пятница, 14 июня 13 г.
Интерфейс должен быть
• Интуитивно понятным• Идеально продуманным с самого
начала
5
пятница, 14 июня 13 г.
С чего начать проектирование?
6
пятница, 14 июня 13 г.
С чего начать проектирование?
6
пятница, 14 июня 13 г.
Просто начать
7
пятница, 14 июня 13 г.
Просто начать
• Отделить API от остальной части проекта– /api
7
пятница, 14 июня 13 г.
Просто начать
• Отделить API от остальной части проекта– /api
• rails generate controller api/posts
7
пятница, 14 июня 13 г.
Бездумное следование
8
пятница, 14 июня 13 г.
Бездумное следование
• В книгах и документации:
def index @people = People.all respond_to do |format| format.html format.xml { render xml: @people.to_xml } end end
8
пятница, 14 июня 13 г.
Приводит к проблемам if @category.save
respond_to do |format| format.html do flash[:notice] = l(:notice_successful_create) redirect_to_settings_in_projects end format.js format.api { render :action => 'show', :status => :created } end else respond_to do |format| format.html { render :action => 'new'} format.js { render :action => 'new'} format.api { render_validation_errors(@category) } end end
9
пятница, 14 июня 13 г.
Правильный путь
10
пятница, 14 июня 13 г.
Правильный путь
10
• respond_with + ( jbuilder || RABL || Action Model Serializers)
пятница, 14 июня 13 г.
Правильный путь
10
• respond_with + ( jbuilder || RABL || Action Model Serializers)
• структура и формат определяются во view или отдельном объекте
пятница, 14 июня 13 г.
Возможный ответ
11
GitHub Instagram Foursquare
пятница, 14 июня 13 г.
Возможный ответ
12
GitHub Instagram Foursquare
пятница, 14 июня 13 г.
Что взять?
13
Foursquare + Instagram
json.meta do |json| json.total @lists.total_count json.num_pages @lists.num_pages json.per_page @per_page end json.items @lists do |json, list| json.partial! "maillist", maillist: listend
пятница, 14 июня 13 г.
Сложные структуры в ответе
14
пятница, 14 июня 13 г.
Сложные структуры в ответе
14
# @return [Hash] Updated subscriber object # @example # { # "id": 1, # "email": "[email protected]", # "first_name": "Name#1", # "last_name": "Name#2", # "organization": "Organization#1", # "accreditation": "Accreditation#1", # "unisender_state": "unprocessed", # "highrise_state": "unprocessed" # } def update_subscriber # some code end
пятница, 14 июня 13 г.
15
JSON Schema
пятница, 14 июня 13 г.
15
"$schema": "http://json-‐schema.org/draft-‐04/schema#", "title": "List", "type": "object", "required": ["id", "name "], "properties": { "id": { "description": "Lists unique identifier", "type": "integer" }, "name": { "description": "Lists name in project", "type": "string" } }
JSON Schema
пятница, 14 июня 13 г.
16
В результате
пятница, 14 июня 13 г.
16
В результате
# @return [Hash] Updated subscriber object # @example # Link: http://yoursite.com/your_schema.json
def update_subscriber # some code end
• Gem: json-schema• https://github.com/hoxworth/json-schema
пятница, 14 июня 13 г.
Пагинация
17
пятница, 14 июня 13 г.
Пагинация
17
• Kaminari• https://github.com/amatsuda/kaminari
def index @lists = List.active. page(params[:page]).per(params[:per])
respond_with @lists end
пятница, 14 июня 13 г.
Выборка, сортировка
18
пятница, 14 июня 13 г.
Выборка, сортировка
18
• Ransack• https://github.com/ernie/ransack
def index @search = List.ransack(params[:search]) @lists = @search.result.page(params[:page]) respond_with @lists end
пятница, 14 июня 13 г.
Обработка ошибок
19
пятница, 14 июня 13 г.
Обработка ошибок
19
• Одно из самых важных мест в API
пятница, 14 июня 13 г.
Обработка ошибок
19
• Одно из самых важных мест в API
• Отдавать правильные статус коды
пятница, 14 июня 13 г.
Обработка ошибок
19
• Одно из самых важных мест в API
• Отдавать правильные статус коды
• Присылать человеческие описания ошибок
пятница, 14 июня 13 г.
Обработка ошибок
19
• Одно из самых важных мест в API
• Отдавать правильные статус коды
• Присылать человеческие описания ошибок
пятница, 14 июня 13 г.
Состояние API
20
пятница, 14 июня 13 г.
Состояние API
20
• Все хорошо - success(200)
пятница, 14 июня 13 г.
Состояние API
20
• Все хорошо - success(200)
• Клиент прислал что-то не то - client error (400)
пятница, 14 июня 13 г.
Состояние API
20
• Все хорошо - success(200)
• Клиент прислал что-то не то - client error (400)
• Сервер сделал что-то не то - server error (500)
пятница, 14 июня 13 г.
Варианты
21
пятница, 14 июня 13 г.
Варианты
21
• 8-10 статус кодов на все случаи жизни
пятница, 14 июня 13 г.
Варианты
21
• 8-10 статус кодов на все случаи жизни
• Улыбаемся и машем
пятница, 14 июня 13 г.
Примеры старших
22
• Facebook:
HTTP Status Code: 200
{"type" : "OauthException", "message":"(#803) Some of the aliases you requested do not exist: foo.bar"}
• Github:
HTTP/1.1 404 Not Found
{"message": "Not Found"}
пятница, 14 июня 13 г.
Exceptions App
23
пятница, 14 июня 13 г.
Exceptions App
23
• application.rb:
– config.exceptions_app = self.routes
пятница, 14 июня 13 г.
Exceptions App
23
• application.rb:
– config.exceptions_app = self.routes
• routes.rb:
– match '/404', :to => "errors#not_found"
– match '/422', :to => "errors#unprocessable_entity"
– match '/500', :to => "errors#internal_error"
пятница, 14 июня 13 г.
Exceptions App
24
class ErrorsController < ApplicationController
respond_to :json, :html def not_found # some code here end def internal_error respond_with(...) end def unprocessable_entity # some code here end end
пятница, 14 июня 13 г.
Версионирование
25
пятница, 14 июня 13 г.
Версионирование
25
• У API обязана быть версия
пятница, 14 июня 13 г.
Версионирование
25
• У API обязана быть версия
• Headers vs Urls
пятница, 14 июня 13 г.
Версионирование
25
• У API обязана быть версия
• Headers vs Urls
• Url:
– http://exmaple.com/v1/users?access_token=...
пятница, 14 июня 13 г.
Версионирование
25
• У API обязана быть версия
• Headers vs Urls
• Url:
– http://exmaple.com/v1/users?access_token=...
• Headers:
– X-Example-Api-Version: 1.2
пятница, 14 июня 13 г.
Сколько версий поддерживать
26
пятница, 14 июня 13 г.
Сколько версий поддерживать
26
• Минимум 1 версию назад
пятница, 14 июня 13 г.
Сколько версий поддерживать
26
• Минимум 1 версию назад
• Зависит от бизнес требований
пятница, 14 июня 13 г.
Best practices || Тест на адекватность
27
пятница, 14 июня 13 г.
Best practices || Тест на адекватность
27
• Библиотека для работы с API
пятница, 14 июня 13 г.
Best practices || Тест на адекватность
27
• Библиотека для работы с API
• Если вам сложно написать клиент для вашего API, стоит серьезно задуматься
пятница, 14 июня 13 г.
API Client
28
пятница, 14 июня 13 г.
API Client
28
• Weary
• https://github.com/mwunsch/weary
class MyClass < Weary::Client
get :resource, "http://host.com/path/to/" do |resource| resource.required :api_token, :id resource.optional :optional_parameter end end
пятница, 14 июня 13 г.
Зачем нужно делать хорошее API?
29
пятница, 14 июня 13 г.
Зачем нужно делать хорошее API?
29
• Залог успешного общения с вашими пользователями
пятница, 14 июня 13 г.
Зачем нужно делать хорошее API?
29
• Залог успешного общения с вашими пользователями
• Неявное вложение в рекламу
пятница, 14 июня 13 г.
Зачем нужно делать хорошее API?
29
• Залог успешного общения с вашими пользователями
• Неявное вложение в рекламу
• Вложение в будущее проекта
пятница, 14 июня 13 г.
Вопросы?
30
пятница, 14 июня 13 г.