Slide 1

Slide 1 text

Devils Tower, WY ROCK SOLID railSAPIS @caike Carlos Souza Code School

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

perfect rails web app Deadline Budget Features Tests

Slide 4

Slide 4 text

“Precisamos de uma interface mais interativa”

Slide 5

Slide 5 text

“Detectamos que a maioria dos visitantes acessa o site a partir de dispositivos móveis. Precisamos lançar um app nativo o mais rápido possível!”

Slide 6

Slide 6 text

“E Aí, TEMOS UMA API ?”

Slide 7

Slide 7 text

• Routes • CONNEG • AUTH • VERSION • Tests*

Slide 8

Slide 8 text

endpoints, resources e url-to-controller mappings ROUTES

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

resources :projects REST Representational
 State Transfer Seguindo um permite a construção de uma infra-estrutura capaz de suportar diferentes tipos de aplicações conjunto estrito de operações $ rake routes Prefix Verb URI Pattern Controller#Action projects GET /projects(.:format) projects#index POST /projects(.:format) projects#create new_project GET /projects/new(.:format) projects#new edit_project GET /projects/:id/edit(.:format) projects#edit project GET /projects/:id(.:format) projects#show PATCH /projects/:id(.:format) projects#update PUT /projects/:id(.:format) projects#update DELETE /projects/:id(.:format) projects#destroy

Slide 11

Slide 11 text

resources :projects end end end member do post 'archive' 
 collection do get 'archived' do

Slide 12

Slide 12 text


 ! get 'active' get 'suspended' ! ! ! post 'activate' post 'suspend' ! ! resources :projects end end end do member do post 'archive' 
 collection do get 'archived' post 'create_review'

Slide 13

Slide 13 text


 ! get 'active' get 'suspended' ! ! ! post 'activate' post 'suspend' ! ! resources :projects end end end do member do post 'archive' 
 collection do get 'archived' , to: 'archived_projects#index' , to: 'active_projects#index' , to: 'suspended_projects#index' , to: 'archived_projects#create' , to: 'active_projects#create' , to: 'suspended_projects#create' post 'create_review'

Slide 14

Slide 14 text


 ! get 'active' get 'suspended' ! ! ! post 'activate' post 'suspend' ! ! resources :projects end end end do member do post 'archive' 
 collection do get 'archived' resources :reviews, only: :create , to: 'archived_projects#index' , to: 'active_projects#index' , to: 'suspended_projects#index' , to: 'archived_projects#create' , to: 'active_projects#create' , to: 'suspended_projects#create'

Slide 15

Slide 15 text

resources :projects end do ... end http://foo.com/api/projects api/projects namespace :api do Prefix Verb URI Pattern Controller#Action api_projects GET /api/projects(.:format) api/projects#index POST /api/projects(.:format) api/projects#create new_api_project GET /api/projects/new(.:format) api/projects#new ...

Slide 16

Slide 16 text

http://foo-dev.com:3000 http://api.foo-dev.com:3000 127.0.0.1 foo-dev.com 127.0.0.1 api.foo-dev.com /etc/hosts , constraints: { subdomain: 'api' }, path: '/' resources :projects end do ... end namespace :api do http://api.foo.com/projects projects api

Slide 17

Slide 17 text

app/controllers/api/projects_controller.rb module Api class ProjectsController < ApplicationController def index ... end end end

Slide 18

Slide 18 text

module API class ProjectsController < ApplicationController def index ... end end end ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym 'API' end config/initializers/inflections.rb

Slide 19

Slide 19 text

module API class ProjectsController < ApplicationController ! ! def index @projects = Project.all end end end module API class SuspendedProjectsController < ApplicationController ! 
 def index @projects = Project.suspended end end end app/controllers/api/projects_controller.rb app/controllers/api/suspended_projects_controller.rb before_action :verify_access before_action :verify_access

Slide 20

Slide 20 text

module API class BaseController < ApplicationController ! ! protected ! def verify_access ... end end end app/controllers/api/base_controller.rb before_action :verify_access before_action :verify_access

Slide 21

Slide 21 text

module API class ProjectsController < ! ! def index @projects = Project.all end end end module API class SuspendedProjectsController < ! 
 def index @projects = Project.suspended end end end BaseController BaseController

Slide 22

Slide 22 text

conneg negociando conteúdo

Slide 23

Slide 23 text

Client A API I’m a mobile phone and I want JSON! Hey, I’m an Enterprise Java Application and I want XML! (Ha Ha, Business!) ¯\_(ツ)_/¯ Hola, yo soy un browser y quiero HTML! response in JSON respuesta in HTML response in XML Client B Client C O processo em que cliente e servidor determinam a melhor representação para a response. content negotiation

Slide 24

Slide 24 text

module API class ProjectsController < ApplicationController respond_to :json, :xml ! def index @projects = Project.all ! respond_with(@projects) end end end Extraída para uma gem a partir do Rails 4.2

Slide 25

Slide 25 text

calls #to_json calls #to_xml module API class ProjectsController < ApplicationController def index @projects = Project.recent ! respond_to do |format| format.json { render json: @projects, status: 200 } format.xml { render xml: @projects, status: 200 } end end end end

Slide 26

Slide 26 text

JBuilder json.content format_content(@message.content) json.(@message, :created_at, :updated_at) ! json.author do json.name @message.creator.name.familiar json.url url_for(@message.creator, format: :json) end https://github.com/rails/jbuilder

Slide 27

Slide 27 text

class ProjectSerializer < ActiveModel::Serializer attributes :id, :title, :amount ! embed :ids, include: true has_many :products end defaults to JSON-API ActiveModel::Serializers https://github.com/rails-api/active_model_serializers

Slide 28

Slide 28 text

module SongsRepresenter include Roar::JSON::JsonApi name :songs ! property :id property :title end class SongRepresenter < Roar::Decorator include Roar::JSON::JsonApi name :songs ! property :id property :title end Roar https://github.com/apotonick/roar usando Mixins usando Decorators

Slide 29

Slide 29 text

AUTHentication Prevenção de acesso não-autorizado a resources protegidos

Slide 30

Slide 30 text

http basic AUTH • Rápido e simples • Reutiliza credenciais existentes • Parte da spec. HTTP (RFC 2617)

Slide 31

Slide 31 text

http basic AUTH module API class ProjectsController < ApplicationController before_action :authenticate_or_request ! protected ! def authenticate_or_request authenticate_or_request_with_http_basic do |user, pwd| User.authenticate(user, pwd) end end end end

Slide 32

Slide 32 text

http basic AUTH com curl use the -u option $ curl -I http://carlos:secret@localhost:3000/projects ! HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 $ curl -Iu 'carlos:secret' http://localhost:3000/projects ! HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8

Slide 33

Slide 33 text

• Can easily expire or regenerate tokens. • Any vulnerability is limited to API access. • Multiple tokens for each user. • Different access rules can be implemented. token based auth API Projects Forum Admin Client A

Slide 34

Slide 34 text

token para a API do Backpack adquirindo o token Normalmente disponível “out of band”,
 na página de profile ou settings.

Slide 35

Slide 35 text

adquirindo o token Em alguns serviços, o token é exibido apenas uma vez por motivo de segurança. token para a API da Digital Ocean

Slide 36

Slide 36 text

module API class ProjectsController < ApplicationController before_action :authenticate_or_request ! protected ! def authenticate_or_request authenticate_or_request_with_http_token do |token, opt| User.find_by(auth_token: token) end end end end token based auth

Slide 37

Slide 37 text

UUID - Universally Unique Identifier GErando TOKENS

Slide 38

Slide 38 text

A Ruby stdlib oferece um método para geração de UUIDs GErando TOKENS SecureRandom.uuid f4ea855f-d303-43e6-bee3-94581c0ecb21 90ab3255-ce33-4022-8349-b7979655b07c 371c760d-2539-41b9-b665-98c255d4c323 ...

Slide 39

Slide 39 text

end def generate_auth_token .gsub(/\-/,'') end GErando TOKENS omits the hyphens SecureRandom.uuid class User < ActiveRecord::Base before_create :set_auth_token ! private ! def set_auth_token return if auth_token.present?
 self.auth_token = generate_auth_token end ! a47a8e54b11c4de5a4a351734c80a14a 9fa8a147b10c4efca3e8592b3a1c2729 823c1c984d504f66a2e6cbb2eb69e842 ...

Slide 40

Slide 40 text

$ curl -IH "Authorization: Token token=16d7d6089b8fe0c5e19bfe10bb156832" \ http://localhost:3000/episodes ! HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 HTTP Request Header token based auth com curl

Slide 41

Slide 41 text

versionamento de resources Version

Slide 42

Slide 42 text

Patch Minor Major versionamento semântico MAJOR
 mudanças incompatíveis 
 MINOR
 novas features backwards-compatible 
 PATCH
 bug fixes http://semver.org/

Slide 43

Slide 43 text

V1 /V1 feature X, feature Y /V2 feature X, feature Y, feature Z VERSIONANDO SERVIÇOS Mudanças não podem quebrar clientes existentes. Exemplo de mudanças compatíveis: • suporte a um novo formato (i.e. JSON, XML) • nova propriedade em um resource Apenas major version. (não necessitam version bump)

Slide 44

Slide 44 text

https://www.mnot.net/blog/2012/12/04/api-evolution “The biggest way to avoid ! new major versions is to make as many of your changes backwards-compatible as possible”

Slide 45

Slide 45 text

TESTS garantindo a consistência da sua api

Slide 46

Slide 46 text

API testando APIs Não é hora de testar regras de negócio (core business) • Status Code • Mime Types • Regras de Acesso Testar Unit Tests

Slide 47

Slide 47 text

API verificando status codes require 'test_helper' ! class ListingProjectsTest < ActionDispatch::IntegrationTest setup { host! 'api.example.com' } ! test 'returns list of projects' do get '/projects' assert_equal 200, response.status refute_empty response.body end end

Slide 48

Slide 48 text

API verificando mime types class ListingProjectsTest < ActionDispatch::IntegrationTest setup { host! 'api.example.com' } 
 test 'returns projects in JSON' do get '/projects', {}, { 'Accept' => Mime::JSON } assert_equal Mime::JSON, response.content_type end test 'returns projects in XML' do get '/projects', {}, { 'Accept' => Mime::XML } assert_equal Mime::XML, response.content_type end end

Slide 49

Slide 49 text

verificando regras de acesso class ListingProjectsTest < ActionDispatch::IntegrationTest setup { @user = User.create! } setup { host! 'api.example.com' } ! test 'valid authentication with token' do get '/projects', {}, { 'Authorization' => "Token token=#{@user.auth_token}"} assert_equal 200, response.status assert_equal Mime::JSON, response.content_type end ! test 'invalid authentication' do get '/projects', {}, { 'Authorization' => "Token token=#{@user.auth_token}fake” } assert_equal 401, response.status end end

Slide 50

Slide 50 text

http://railsapis.codeschool.com/

Slide 51

Slide 51 text

ROCK SOLID railSAPIS OBRIGADO! https://www.flickr.com/photos/9464735@N06/5978699502