construindo apis usando rails

Post on 10-May-2015

969 Views

Category:

Technology

4 Downloads

Preview:

Click to see full reader

DESCRIPTION

Essa apresentação aconteceu no 17 encontro dos usuários de Rails de PE - Frevo on Rails. Nela, eu abordo 4 tópicos importantes na hora de implementar APIs: Rest, Consistencia, Versionamento e Segurança.

TRANSCRIPT

Criando APIs - Usando RailsFernando KakimotoDesenvolvedor @ ThoughtWorks

O que é uma API mesmo?

Application Programming Interface

Biblioteca que inclui especificações para rotinas, estruturas de dados, objetos, classes e

variáveis

Características de boas APIs

Fácil de aprender e memorizar

Dificilmente usada de maneira errada

Minimalista

Completa

Tópicos para hoje

REST

Consistência

Versionamento

Segurança

REST

REpresentational State Transfer

Um padrão arquitetural para sistemas hipermídias distribuídos

REST

URL base

Tipo de mídia

Operações (Método HTTP)

REST

GET https://api.twitter.com/1.1/statuses/ment

ions_timeline.json

REST

GET https://api.twitter.com/1.1/statuses/ment

ions_timeline.json

URL base

REST

GET https://api.twitter.com/1.1/statuses/ment

ions_timeline.jsonTipo de mídia

REST

GET https://api.twitter.com/1.1/statuses/ment

ions_timeline.json

Método

2 URLs base por recurso

• GET /tickets - recupera conjunto de tickets

• GET /tickets/12 - recupera o ticket #12

• POST /tickets - cria um novo ticket

• PUT /tickets/12 - atualiza ticket #12

• DELETE /tickets/12 - remove ticket #12

Restrições REST

Cliente-Servidor

Stateless

Cacheable

Sistema em camadas

Interface Uniforme

API RESTful

API implementada usando princípios HTTP e REST

> nandokakimoto@Development$ rails new blog

> nandokakimoto@blog$ rails generate scaffold Post name:string title:string content:text

> nandokakimoto@blog$ rake routes

posts GET /posts(.:format) posts#index POST /posts(.:format) posts#create new_post GET /posts/new(.:format) posts#new edit_post GET /posts/:id/edit(.:format) posts#edit post GET /posts/:id(.:format) posts#show PUT /posts/:id(.:format) posts#update DELETE /posts/:id(.:format) posts#destroy

Consistência

Métodos Safe

Métodos que nunca modificam um recurso

Dados não devem ser alterados como resultado de uma requisição GET

Métodos Idempotentes

Métodos com o mesmo resultado mesmo que requisição seja repetida

diversas vezesPOST é o único método não idempotente

Código de Resposta

Respostas HTTP padronizam como informar o cliente sobre o resultado da

sua requisição

Código de Resposta

1xx - Informacional

2xx - Sucesso

3xx - Redirecionamento

4xx - Erro de cliente

5xx - Erro de servidor

> nandokakimoto@blog$ curl -v http://localhost:3000/posts.json

* Connected to localhost (127.0.0.1) port 3000 (#0)> GET /posts.json HTTP/1.1<< HTTP/1.1 200 OK < Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)[{"content": "Melhor evento ever <3 <3 <3", "created_at": "2013-09-03T02:12:26Z", "id": 1, "name": "Frevo on Rails", "title": "Evento 14 Setembro", "updated_at": "2013-09-03T02:12:26Z"}]

> nandokakimoto@blog$ curl -v -d "post[name]=Geek Night&post[title]=Scala Dojo&post[content]=Come and learn Scala" http://localhost:3000/posts.json

* Connected to localhost (127.0.0.1) port 3000 (#0)> POST /posts.json HTTP/1.1> Host: localhost:3000<< HTTP/1.1 201 Created < Location: http://localhost:3000/posts/2< Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27){"content": "Come and learn Scala", "created_at": "2013-09-04T01:19:33Z", "id”: 2, "name": "Geek Night", "title":"Scala Dojo", "updated_at": "2013-09-04T01:19:33Z"}

> nandokakimoto@blog$ curl -v -d "post[name]=Geek Night&post[content]=Come and learn Scala" http://localhost:3000/posts.json

* Connected to localhost (127.0.0.1) port 3000 (#0)> POST /posts.json HTTP/1.1> Host: localhost:3000> Content-Length: 56> < HTTP/1.1 422 < Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)< Content-Length: 28{"title":["can't be blank"]}

Versionamento

Versionamento

Sempre versione a sua APIDiferentes opniões sobre se a versão deve estar na URL ou no

cabeçalho

Versionamento

Versionist - https://github.com/bploetz/versionist

RocketPants - https://github.com/filtersquad/rocket_pants

config/routes.rb

> nandokakimoto@blog$ rake routes

(…)

GET /api/v1/products(.:format) api/v1/posts#indexPOST /api/v1/products(.:format) api/v1/posts#createGET /api/v1/products/new(.:format) api/v1/posts#newGET /api/v1/products/:id/edit(.:format) api/v1/posts#editGET /api/v1/products/:id(.:format) api/v1/posts#showPUT /api/v1/products/:id(.:format) api/v1/posts#updateDELETE /api/v1/products/:id(.:format) api/v1/posts#destroy

app/controllers/api/v1/posts_controller.rb

> nandokakimoto@blog$ curl -v http://localhost:3000/api/v1/posts.json

* Connected to localhost (127.0.0.1) port 3000 (#0)> GET /api/v1/posts HTTP/1.1> Host: localhost:3000<< HTTP/1.1 200 OK < Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)< Content-Length: 330[{"content": "Melhor evento ever <3 <3 <3", "created_at":"2013-09-03T02:12:26Z", "id": 1, "name": "Frevo on Rails", "title": "Evento 14 Setembro", "updated_at": "2013-09-03T02:12:26Z"}, {"content": "Come and learn Scala", "created_at": "2013-09-04T01:19:33Z", "id": 2, "name": "Geek Night", "title": "Scala Dojo", "updated_at": "2013-09-04T01:19:33Z"}]

> nandokakimoto@blog$ curl -v http://localhost:3000/api/v1/posts/15.json

* Connected to localhost (127.0.0.1) port 3000 (#0)> GET /api/v1/posts/15.json HTTP/1.1> Host: localhost:3000> < HTTP/1.1 404 Not Found < Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)< Content-Length: 1<

Versionamento

Novo RequisitoMostrar apenas id, nome e título do post

config/routes.rb

> nandokakimoto@blog$ rake routes

(…)

GET /api/v2/posts(.:format) api/v2/posts#index POST /api/v2/posts(.:format) api/v2/posts#create GET /api/v2/posts/new(.:format) api/v2/posts#new GET /api/v2/posts/:id/edit(.:format) api/v2/posts#edit GET /api/v2/posts/:id(.:format) api/v2/posts#show PUT /api/v2/posts/:id(.:format) api/v2/posts#update DELETE /api/v2/posts/:id(.:format) api/v2/posts#destroy

app/controllers/api/v2/posts_controller.rb

RABL

RABL (Ruby API Builder Language) consiste num sistema de template RAILS para geração de JSON

app/views/api/v2/show.rabl.json

app/views/api/v2/index.rabl.json

> nandokakimoto@blog$ curl -v http://localhost:3000/api/v2/posts

* Connected to localhost (127.0.0.1) port 3000 (#0)> GET /api/v2/posts HTTP/1.1> Host: localhost:3000<< HTTP/1.1 200 OK < Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)< Content-Length: 113[{"id":1,"name":"Frevo on Rails","title":"Evento 14 Setembro"},{"id”:2,"name":"Geek Night","title":"Scala Dojo"}]

Segurança

HTTP Basic Authentication

Solução mais simples

Fácil de implementar

Maioria dos clientes suportam

app/controllers/api/v2/posts_controller.rb

> nandokakimoto@blog$ curl -v http://localhost:3000/api/v2/posts

* Connected to localhost (127.0.0.1) port 3000 (#0)> GET /api/v2/posts HTTP/1.1> Host: localhost:3000<< HTTP/1.1 200 OK < Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)< Content-Length: 113[{"id":1,"name":"Frevo on Rails","title":"Evento 14 Setembro"},{"id”:2,"name":"Geek Night","title":"Scala Dojo"}]

> nandokakimoto@blog$ curl -v -d "post[name]=RubyConf&post[title]=RubyConf Details&post[content]=RubyConf was awesome" http://localhost:3000/api/v2/posts.json

* Connected to localhost (127.0.0.1) port 3000 (#0)> POST /api/v2/posts.json HTTP/1.1> Host: localhost:3000> Content-Length: 82> < HTTP/1.1 401 Unauthorized < WWW-Authenticate: Basic realm="Application"< Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)< Content-Length: 27< HTTP Basic: Access denied.

> nandokakimoto@blog$ curl -v -d "post[name]=RubyConf&post[title]=RubyConf Details&post[content]=RubyConf was awesome" http://localhost:3000/api/v2/posts.json -u "admin:secret"

* Connected to localhost (127.0.0.1) port 3000 (#0)* Server auth using Basic with user 'admin'> POST /api/v2/posts.json HTTP/1.1> Authorization: Basic YWRtaW46c2VjcmV0> Host: localhost:3000> Content-Length: 83> < HTTP/1.1 201 Created< Location: http://localhost:3000/api/v2/posts/3 < Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)< Content-Length: 159{"id":3,"name":"RubyConf","title":"RubyConf Details”}

Token de Acesso

Maior entropia

Token é salvo no servidor

Data de expiração

> nandokakimoto@blog$ rails g model api_key access_tokeninvoke active_record

create db/migrate/20130907211645_create_api_keys.rb create app/models/api_key.rb invoke test_unit create test/unit/api_key_test.rb create test/fixtures/api_keys.yml

app/model/api_key.rb

app/controllers/api/v2/posts_controller.rb

> nandokakimoto@blog$ curl -v http://localhost:3000/api/v2/posts

curl -v http://localhost:3000/api/v2/posts* Connected to localhost (127.0.0.1) port 3000 (#0)> GET /api/v2/posts HTTP/1.1> Host: localhost:3000> < HTTP/1.1 401 Unauthorized < WWW-Authenticate: Token realm="Application"< Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)< Content-Length: 27< HTTP Token: Access denied.

> nandokakimoto@blog$ curl -v http://localhost:3000/api/v2/posts -H 'Authorization: Token token=”8219a125816b331d0e478eeab566bf7c”'

* Connected to localhost (127.0.0.1) port 3000 (#0)> GET /api/v2/posts HTTP/1.1> Host: localhost:3000> Authorization: Token token="8219a125816b331d0e478eeab566bf7c"> < HTTP/1.1 200 OK < Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-06-27)< Content-Length: 168[{"id":1,"name":"Frevo on Rails","title":"Evento 14 Setembro"},{"id":2,"name":"Geek Night","title":"Scala Dojo"},{"id":3,"name":"RubyConf","title":"RubyConf Details"}]

OAuth

“An open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop application”

- http://oauth.net -

OAuth

Doorkeeper

Oauth2

Tópicos Futuros

Filtros, ordenação, busca

Paginação

Documentação

Limitação de uso

Pra Terminar

API é a intefarce de usuário para os desenvolvedores

Trabalhe para garantir que ela seja funcional e prazerosa de usar

top related