Diseño de APIs con Ruby

  • 789 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
789
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
14
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Diseño de APIs con Ruby Edwin Cruz @softr8 #SGRubyFriday, June 15, 2012
  • 2. PLAN • Que es una API • Como implementar una buena API • Usando Ruby on Rails para implementar una API • Patrones de diseñoFriday, June 15, 2012
  • 3. “ Application Programming Interface “Friday, June 15, 2012
  • 4. API Es una manera para comunicar dos aplicaciones entre ellas.Friday, June 15, 2012
  • 5. TIPOS DE API • Library • SDK • Web servicesFriday, June 15, 2012
  • 6. PRIMERAS API - SOAP Simple Object Access ProtocolFriday, June 15, 2012
  • 7. REST REpresentation State TransferFriday, June 15, 2012
  • 8. CRUD Create Altas Read Bajas Update Cambios Delete ConsultasFriday, June 15, 2012
  • 9. REST ⋍ CRUDFriday, June 15, 2012
  • 10. REST ⋍ CRUD Recursos a través de URI Uso de verbos HTTPFriday, June 15, 2012
  • 11. REST-ISH + JSONFriday, June 15, 2012
  • 12. REST-ISH + JSON =Friday, June 15, 2012
  • 13. REST-ISH + JSON = Cosas IncreiblesFriday, June 15, 2012
  • 14. RECURSOFriday, June 15, 2012
  • 15. RECURSOCualquier cosa expuesta mediante webFriday, June 15, 2012
  • 16. RECURSOCualquier cosa expuesta mediante webTienen una representación en datosFriday, June 15, 2012
  • 17. RECURSOCualquier cosa expuesta mediante webTienen una representación en datosCon un servicio web intercambiamosrepresentaciones de recursosFriday, June 15, 2012
  • 18. REQUISITOS RESTFriday, June 15, 2012
  • 19. REQUISITOS RESTSeparación de responsabilidadesFriday, June 15, 2012
  • 20. REQUISITOS RESTSeparación de responsabilidadesCliente/ServidorFriday, June 15, 2012
  • 21. REQUISITOS RESTSeparación de responsabilidadesCliente/ServidorSin estadoFriday, June 15, 2012
  • 22. REQUISITOS RESTSeparación de responsabilidadesCliente/ServidorSin estado“Cacheable”Friday, June 15, 2012
  • 23. REQUISITOS RESTSeparación de responsabilidadesCliente/ServidorSin estado“Cacheable”Sistema a capasFriday, June 15, 2012
  • 24. REQUISITOS RESTSeparación de responsabilidadesCliente/ServidorSin estado“Cacheable”Sistema a capasInterface uniformeFriday, June 15, 2012
  • 25. ¿PORQUÉ UN API?Friday, June 15, 2012
  • 26. ¿PORQUÉ UN API? Aumenta la flexibilidad de un servicioFriday, June 15, 2012
  • 27. ¿PORQUÉ UN API? Aumenta la flexibilidad de un servicio Aumenta la utilidad de un servicioFriday, June 15, 2012
  • 28. ¿PORQUÉ UN API? Aumenta la flexibilidad de un servicio Aumenta la utilidad de un servicio Libera los datos de usuarioFriday, June 15, 2012
  • 29. ¿PORQUÉ UN API? Aumenta la flexibilidad de un servicio Aumenta la utilidad de un servicio Libera los datos de usuario Proporciona valor de negocioFriday, June 15, 2012
  • 30. ¿QUÉ CARACTERÍSTICAS?Friday, June 15, 2012
  • 31. ¿QUÉ CARACTERÍSTICAS? Fácil de implementar y mantenerFriday, June 15, 2012
  • 32. ¿QUÉ CARACTERÍSTICAS? Fácil de implementar y mantener Buen rendimientoFriday, June 15, 2012
  • 33. ¿QUÉ CARACTERÍSTICAS? Fácil de implementar y mantener Buen rendimiento EscalableFriday, June 15, 2012
  • 34. ¿QUÉ CARACTERÍSTICAS? Fácil de implementar y mantener Buen rendimiento Escalable Fácil de entender y usarFriday, June 15, 2012
  • 35. ¿CUÁLES SON LOS RETOS?Friday, June 15, 2012
  • 36. ¿CUÁLES SON LOS RETOS?La red es un eslabón débilFriday, June 15, 2012
  • 37. ¿CUÁLES SON LOS RETOS?La red es un eslabón débilAPI incompleta pone estrés en el clienteFriday, June 15, 2012
  • 38. ¿CUÁLES SON LOS RETOS?La red es un eslabón débilAPI incompleta pone estrés en el clienteAdministrar CambiosFriday, June 15, 2012
  • 39. DISEÑAR UNA BUENA APIFriday, June 15, 2012
  • 40. CONVENCIONES RESTFriday, June 15, 2012
  • 41. REST ⋍ CRUD GET /api/products #Listado {"products"  :      [        {"product"  :  {  "id"  :  1,  "name"  :  "Producto  1",  "status"  :  "archived"}  },        {"product"  :  {  "id"  :  2,  "name"  :  "Producto  2",  "status"  :  "active"    }  }    ] }Friday, June 15, 2012
  • 42. REST ⋍ CRUD GET /api/products/2 #Ver {"product"  :  {  "id"  :  2,  "name"  :  "Producto  2",  "status"  :  "active"    }  }Friday, June 15, 2012
  • 43. REST ⋍ CRUD POST /api/products #Crear {    "name"  :  "Producto  2" }Friday, June 15, 2012
  • 44. REST ⋍ CRUD PUT /api/products/2 #Actualizar {    "name"  :  "Producto  dos" }Friday, June 15, 2012
  • 45. REST ⋍ CRUD DELETE /api/products/2 #EliminarFriday, June 15, 2012
  • 46. VERSIONANDO TU API • API Interna (entre aplicaciones) • API Externa (aplicacion movil ? ) • API Usuarios (publico en general)Friday, June 15, 2012
  • 47. VERSIONANDO TU API /int/api/products /ext/api/products /pub/api/productsFriday, June 15, 2012
  • 48. VERSIONANDO TU API /int/api/products?version=2 /ext/api/products?version=2 /pub/api/products?version=2Friday, June 15, 2012
  • 49. VERSIONANDO TU API /v2/int/api/products /v2/ext/api/products /v2/pub/api/productsFriday, June 15, 2012
  • 50. MUNDO IDEALFriday, June 15, 2012
  • 51. MUNDO IDEALUso de: Accept HeaderFriday, June 15, 2012
  • 52. MUNDO IDEALUso de: Accept HeaderAccept: application/vnd.mycompany.com;version=2,application/jsonFriday, June 15, 2012
  • 53. CODIGOS HTTP 200 OK 201 Created 202 Accepted 400 Bad Request 401 Unauthorized 402 Payment Required 404 Not Found 409 Conflict 422 Unprocessable Entity 500 Internal Server Error 503 Service UnavailableFriday, June 15, 2012
  • 54. CODIGOS HTTP HTTP/1.1 401 Unauthorized { “errors”: [ “api_key not found” ] }Friday, June 15, 2012
  • 55. CODIGOS HTTP HTTP/1.1 401 Unauthorized { “errors”: [ “api_key no valida”, “api_key no puede contener espacios” ] }Friday, June 15, 2012
  • 56. CODIGOS HTTP HTTP/1.1 401 Unauthorized { “errors”: [ “api_key no encontrada, por favor visita http://account.myapp.com/api para obtenerla” ] }Friday, June 15, 2012
  • 57. CODIGOS HTTP HTTP/1.1 400 Bad Request { “errors”: [ “Estructura JSON no valida”, “unexpected TSTRING, expected ‘}’ at line 2” ] }Friday, June 15, 2012
  • 58. DOCUMENTACION HTTP/1.1 422 Unprocessable Entity { “errors”: [ “vendor_code: no puede estar vacio” ], “documentacion”: [ “vendor_code”: [ “descripcion” : “Codigo asignado por proveedor”, “formato” : “Combinacion de tres letras seguidas de 4 numeros”, “ejemplo” : “SOL1234” ] ] }Friday, June 15, 2012
  • 59. CODIGOS HTTP HTTP/1.1 503 Service Unavailable { “messages”: [ “En mantenimiento” ] }Friday, June 15, 2012
  • 60. OPCIONES AVANZADAS • Simuladores • Autenticación • Validadores • Limite de uso • Rapidez • BalanceoFriday, June 15, 2012
  • 61. USANDO RUBY ON RAILS PARA IMLEMENTAR UNA APIFriday, June 15, 2012
  • 62. APIS ON RAILS - REST MyApp::Application.routes.draw do resources :products end products GET /products(.:format) products#index POST /products(.:format) products#create new_product GET /products/new(.:format) products#new edit_product GET /products/:id/edit(.:format) products#edit product GET /products/:id(.:format) products#show PUT /products/:id(.:format) products#update DELETE /products/:id(.:format) products#destroyFriday, June 15, 2012
  • 63. APIS ON RAILS - REST MyApp::Application.routes.draw do scope ‘/api’ do resources :products end end products GET /api/products(.:format) products#index POST /api/products(.:format) products#create new_product GET /api/products/new(.:format) products#new edit_product GET /api/products/:id/edit(.:format) products#edit product GET /api/products/:id(.:format) products#show PUT /api/products/:id(.:format) products#update DELETE /api/products/:id(.:format) products#destroyFriday, June 15, 2012
  • 64. APIS ON RAILS - gem versionist VERSIONES MyApp::Application.routes.draw do api_version(:module => V1, :path => v2) do resources :products end end v2_products GET /v2/products(.:format) V1/products#index POST /v2/products(.:format) V1/products#create new_v2_product GET /v2/products/new(.:format) V1/products#new edit_v2_product GET /v2/products/:id/edit(.:format) V1/products#edit v2_product GET /v2/products/:id(.:format) V1/products#show PUT /v2/products/:id(.:format) V1/products#update DELETE /v2/products/:id(.:format) V1/products#destroyFriday, June 15, 2012
  • 65. APIS ON RAILS, CONTROLADOR class V2::ProductsController < ApplicationController respond_to :json def index @products = V2::Product.paginate(:page => (params[:page] || 1), :per_page => (params[:per_page] || 100)).all respond_with @products end def show @product = V2::Product.find(params[:id]) respond_with @product end def update @product = V2::Product.find(params[:id]) @product.update_attributes(params[:product]) respond_with @product end def destroy @product = V2::Product.find(params[:id]) respond_with @product.destroy end endFriday, June 15, 2012
  • 66. APIS ON RAILS - MODELO class V2::Product < Product JSON_ATTRIBUTES = { properties: [ :id, :upc, :sku, :list_cost, :color, :dimension, :size, :created_at, :updated_at, ], methods: [ :units_on_hand ] } endFriday, June 15, 2012
  • 67. APIS ON RAILS, CONTROLADOR gem ‘rabl’ class V3::ProductsController < ApplicationController respond_to :json, :xml def index @products = V3::Product.paginate(:page => (params[:page] || 1), :per_page => (params[:per_page] || 100)).all end def show @product = V3::Product.find(params[:id]) end def update @product = V3::Product.find(params[:id]) @product.update_attributes(params[:product]) end def destroy @product = V3::Product.find(params[:id]) render json: {}, status: @product.destroy ? :ok : :unprocessable_entity end endFriday, June 15, 2012
  • 68. APIS ON RAILS VISTAS #app/views/api/v3/products/index.rabl collection @products attributes :id, :name, :status node(:url) {|product| product_url(product) } node(:current_stock) {|product| product.variants.map(&:on_hand).sum } child :variants do attributes :upc, :color, :size, :on_hand end {"products"  :      [        {"product"  :  {  "id"  :  1,  "name"  :  "Producto  1",  "status"  :  "archived",  “current_stock”  :  10,            “variants”  :  [  {“upc”  :  “ASDFS”,  “color”  :  “negro”,  “size”  :  “M”,  “on_hand”  :  10}  ]            }        }    ] }Friday, June 15, 2012
  • 69. APIS ON RAILS VISTAS gem ‘jbuilder’ Jbuilder.encode do |json| json.content format_content(@message.content) json.(@message, :created_at, :updated_at) json.author do |json| json.name @message.creator.name.familiar json.email_address @message.creator.email_address_with_name json.url url_for(@message.creator, format: :json) end if current_user.admin? json.visitors calculate_visitors(@message) end json.comments @message.comments, :content, :created_at endFriday, June 15, 2012
  • 70. APIS ON RAILS VISTAS gem ‘active_model_serializer’ class PostSerializer < ActiveModel::Serializer attributes :id, :body attribute :title, :key => :name has_many :comments def tags tags.order :name end endFriday, June 15, 2012
  • 71. APIS ON RAILS SEGURIDAD Devise Autenticacion Flexible para aplicaciones Rails Compuesta de 12 modulos: database authenticable, token authenticable, omniauthable, confirmable, recoverable, registerable, trackable, timeoutable, validatable, lockableFriday, June 15, 2012
  • 72. APIS ON RAILS SEGURIDAD Devise Autenticacion Flexible para aplicaciones Rails Compuesta de 12 modulos: database authenticable, token authenticable, omniauthable, confirmable, recoverable, registerable, trackable, timeoutable, validatable, lockableFriday, June 15, 2012
  • 73. APIS ON RAILS SEGURIDAD gem ‘rabl’ class V3::ProductsController < ApplicationController before_filter :authenticate_user! respond_to :json, :xml def index @products = V3::Product.paginate(:page => (params[:page] || 1), :per_page => (params[:per_page] || 100)).all end def show @product = V3::Product.find(params[:id]) end def update @product = V3::Product.find(params[:id]) @product.update_attributes(params[:product]) end def destroy @product = V3::Product.find(params[:id]) render json: {}, status: @product.destroy ? :ok : :unprocessable_entity end endFriday, June 15, 2012
  • 74. APIS ON RAILS PRUEBAS describe V3::ProductsController do before do @request.env["HTTP_ACCEPT"] = "application/json" end describe "#index" do context "cuando no se pasa ningun atributo" do it "regresa los registros en paginas" do get :index response.should be_success data = JSON.parse(response.body) Product.count.should > 0 data[products].length.should == Product.count end end end endFriday, June 15, 2012
  • 75. APIS ON RAILS PRUEBAS describe V3::ProductsController do before do @request.env["HTTP_ACCEPT"] = "application/json" end describe "#show" do context "pasando un id inexistente" do it "responde con http 404 y un mensaje de error" do get :show, id: -1 response.code.should == "404" JSON.parse(response.body)[error].should == "ActiveRecord::RecordNotFound" JSON.parse(response.body)[message].should == "Couldnt find Product with id=-1" end end end endFriday, June 15, 2012
  • 76. APIS ON RAILS PRUEBAS describe V3::ProductsController do before do @request.env["HTTP_ACCEPT"] = "application/json" end describe "#create" do context "con malos atributos" do it "responde con un error" do post :create, product: {bad_key: "foo"} response.code.should == "400" json_response = JSON.parse(response.body) json_response[error].should == "ActiveRecord::UnknownAttributeError" json_response[message].should == "unknown attribute: bad_key" end end end endFriday, June 15, 2012
  • 77. APIS ON RAILS PRUEBAS describe V3::ProductsController do before do @request.env["HTTP_ACCEPT"] = "application/json" end describe "#create" do context "con atributos correctos" do it "responde correctamente y crea el producto" do expect { post :create, product: {name: "productito"} }.to change(Product, :count).by(1) end end end endFriday, June 15, 2012
  • 78. RAILS A DIETAFriday, June 15, 2012
  • 79. RAILS A DIETA Rails es modularFriday, June 15, 2012
  • 80. RAILS A DIETA Rails es modular Para crear APIs, algunos Middlewares no son necesariosFriday, June 15, 2012
  • 81. RAILS A DIETA Rails es modular Para crear APIs, algunos Middlewares no son necesarios rails-apiFriday, June 15, 2012
  • 82. <Module:0x007ff271221e40>, ActionDispatch::Routing::Helpers, #<Module:0x007ff2714ad268>, ActionController::Base, ActionDispatch::Routing::RouteSet::MountedHelpers, HasScope, ActionController::Compatibility, ActionController::ParamsWrapper, ActionController::Instrumentation, ActionController::Rescue, ActiveSupport::Rescuable, ActionController::HttpAuthentication::Token::ControllerMethods, ActionController::HttpAuthentication::Digest::ControllerMethods, ActionController::HttpAuthentication::Basic::ControllerMethods, ActionController::RecordIdentifier, ActionController::DataStreaming, ActionController::Streaming, ActionController::ForceSSL, ActionController::RequestForgeryProtection, AbstractController::Callbacks, ActiveSupport::Callbacks, ActionController::Flash, ActionController::Cookies, ActionController::MimeResponds, ActionController::ImplicitRender, ActionController::Caching, ActionController::Caching::Fragments, ActionController::Caching::ConfigMethods, ActionController::Caching::Pages, ActionController::Caching::Actions, ActionController::ConditionalGet, ActionController::Head, ActionController::Renderers::All, ActionController::Renderers, ActionController::Rendering, ActionController::Redirecting, ActionController::RackDelegation, ActiveSupport::Benchmarkable, AbstractController::Logger, ActionController::UrlFor, AbstractController::UrlFor, ActionDispatch::Routing::UrlFor, ActionDispatch::Routing::PolymorphicRoutes, ActionController::HideActions, ActionController::Helpers, AbstractController::Helpers, AbstractController::AssetPaths, AbstractController::Translation, AbstractController::Layouts, AbstractController::Rendering, AbstractController::ViewPaths, ActionController::Metal, AbstractController::Base, ActiveSupport::Configurable, Object, ActiveSupport::Dependencies::Loadable, Mongoid::Extensions::Object::Yoda, Mongoid::Extensions::Object::Substitutable, Mongoid::Extensions::Object::Reflections, Mongoid::Extensions::Object::DeepCopy, Mongoid::Extensions::Object::Checks, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObjectFriday, June 15, 2012
  • 83. <Module:0x007ff271221e40>, ActionDispatch::Routing::Helpers, #<Module:0x007ff2714ad268>, ActionController::Base, ActionDispatch::Routing::RouteSet::MountedHelpers, HasScope, ActionController::Compatibility, ActionController::ParamsWrapper, ActionController::Instrumentation, ActionController::Rescue, ActiveSupport::Rescuable, ActionController::HttpAuthentication::Token::ControllerMethods, ActionController::HttpAuthentication::Digest::ControllerMethods, ActionController::HttpAuthentication::Basic::ControllerMethods, ActionController::RecordIdentifier, #<Module:0x007f9211d5cd70>, ActionController::DataStreaming, ActionDispatch::Routing::Helpers, ActionController::Streaming, #<Module:0x007f9211f7b5e8>, ActionController::ForceSSL, ActionController::API, ActionController::RequestForgeryProtection, ActiveRecord::Railties::ControllerRuntime, AbstractController::Callbacks, ActionDispatch::Routing::RouteSet::MountedHelpers, ActiveSupport::Callbacks, ActionController::Instrumentation, ActionController::Flash, ActionController::Rescue, ActionController::Cookies, ActiveSupport::Rescuable, ActionController::MimeResponds, ActionController::DataStreaming, ActionController::ImplicitRender, ActionController::ForceSSL, ActionController::Caching, AbstractController::Callbacks, ActionController::Caching::Fragments, ActiveSupport::Callbacks, ActionController::Caching::ConfigMethods, ActionController::ConditionalGet, ActionController::Caching::Pages, ActionController::Head, ActionController::Caching::Actions, ActionController::Renderers::All, ActionController::ConditionalGet, ActionController::Renderers, ActionController::Head, ActionController::Rendering, ActionController::Renderers::All, AbstractController::Rendering, ActionController::Renderers, AbstractController::ViewPaths, ActionController::Rendering, ActionController::Redirecting, ActionController::Redirecting, ActionController::RackDelegation, ActionController::RackDelegation, ActiveSupport::Benchmarkable, ActiveSupport::Benchmarkable, AbstractController::Logger, AbstractController::Logger, ActionController::UrlFor, ActionController::UrlFor, AbstractController::UrlFor, AbstractController::UrlFor, ActionDispatch::Routing::UrlFor, ActionDispatch::Routing::UrlFor, ActionDispatch::Routing::PolymorphicRoutes, ActionDispatch::Routing::PolymorphicRoutes, ActionController::HideActions, ActionController::HideActions, ActionController::Metal, ActionController::Helpers, AbstractController::Base, AbstractController::Helpers, ActiveSupport::Configurable, AbstractController::AssetPaths, Object, AbstractController::Translation, JSON::Ext::Generator::GeneratorMethods::Object, AbstractController::Layouts, ActiveSupport::Dependencies::Loadable, AbstractController::Rendering, PP::ObjectMixin, AbstractController::ViewPaths, Kernel, ActionController::Metal, BasicObject AbstractController::Base, ActiveSupport::Configurable, Object, ActiveSupport::Dependencies::Loadable, Mongoid::Extensions::Object::Yoda, Mongoid::Extensions::Object::Substitutable, Mongoid::Extensions::Object::Reflections, Mongoid::Extensions::Object::DeepCopy, Mongoid::Extensions::Object::Checks, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObjectFriday, June 15, 2012
  • 84. use ActionDispatch::Staticuse Rack::Lockuse#<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fd3b32928c0>use Rack::Runtimeuse Rack::MethodOverrideuse ActionDispatch::RequestIduse Rails::Rack::Loggeruse ActionDispatch::ShowExceptionsuse ActionDispatch::DebugExceptionsuse ActionDispatch::RemoteIpuse ActionDispatch::Reloaderuse ActionDispatch::Callbacksuse ActionDispatch::CookiesuseActionDispatch::Session::CookieStoreuse ActionDispatch::Flashuse ActionDispatch::ParamsParseruse ActionDispatch::Headuse Rack::ConditionalGetuse Rack::ETaguseActionDispatch::BestStandardsSupportuseRack::Mongoid::Middleware::IdentityMapFriday, June 15, 2012
  • 85. use ActionDispatch::Staticuse Rack::Lockuse#<ActiveSupport::Cache::Strategy::Loc use ActionDispatch::StaticalCache::Middleware:0x007fd3b32928c0> use Rack::Lockuse Rack::Runtime useuse Rack::MethodOverride #<ActiveSupport::Cache::Strategy::LocalCacuse ActionDispatch::RequestId he::Middleware:0x007fe74448cf50>use Rails::Rack::Logger use Rack::Runtimeuse ActionDispatch::ShowExceptions use ActionDispatch::RequestIduse ActionDispatch::DebugExceptions use Rails::Rack::Loggeruse ActionDispatch::RemoteIp use ActionDispatch::ShowExceptionsuse ActionDispatch::Reloader use ActionDispatch::DebugExceptionsuse ActionDispatch::Callbacks use ActionDispatch::RemoteIpuse ActionDispatch::Cookies use ActionDispatch::Reloaderuse use ActionDispatch::CallbacksActionDispatch::Session::CookieStore useuse ActionDispatch::Flash ActiveRecord::ConnectionAdapters::Connectiuse ActionDispatch::ParamsParser onManagementuse ActionDispatch::Head use ActiveRecord::QueryCacheuse Rack::ConditionalGet use ActionDispatch::ParamsParseruse Rack::ETag use ActionDispatch::Headuse use Rack::ConditionalGetActionDispatch::BestStandardsSupport use Rack::ETaguseRack::Mongoid::Middleware::IdentityMapFriday, June 15, 2012
  • 86. Gracias! Preguntas? Edwin Cruz edwin@crowdint.com @softr8Friday, June 15, 2012