Api devconf 2013

Preview:

Citation preview

Проектирование API

Игорь Кузнецов, Undev, 2013igkuznetsov@gmail.com, @igkuz

1

пятница, 14 июня 13 г.

План

• Что такое 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":  "123@example.com",    #              "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 г.