30-50% off hundreds of digital assets! WordPress themes, video, music and more 30-50% Off Go to Sale
Advertisement
  1. Code
  2. Ruby on Rails
Code

Criando APIs Com Rails

by
Length:LongLanguages:

Portuguese (Português) translation by Erick Patrick (you can also view the original English article)

Hoje em dia, é prática comum depender bastante de APIs (interfaces programáveis de aplicação). Não só grande serviços como Facebook e Twitter as usam—APIs são bem populares dada a gama de framework do lado do cliente, como React Angular e outras. Ruby on Rails segue a tendência e sua última versão apresenta um novo recurso que permite criar aplicações só API.

Inicialmente, essa funcionalidade era fornecida em uma gem separada chamada rails-api, mas desde Rails 5, é parte da base do framework. Esse curso, junto de ActionCable foi, provavelmente, o mais esperado e, hoje, iremos discuti-lo.

O artigo cobre como criar uma aplicação Rails só API e explica como estruturar as rotas e controladores, responder com JSON, adicionar serializadores e configurar CORS (Cross Origin Resource Sharing). Também aprenderemos sobre opções de segurança da API e protegê-la de abusos.

O código fonte do artigo está disponível no GitHub.

Criando uma Aplicação só API

Para começar, executemos o comando a seguir no terminal:

Ele criará uma aplicação Rails só API chamada RailsApiDemo. Não esqueçamos que a opção --api só funciona em Rails 5, então é bom certificar-se de tê-la instalada.

Abramos o Gemfile e vejamos que é bem menor que o normal: gem como coffer-rails, turbolinks e sass-rails não estão presentes.

O arquivo config/application.rb contém uma linha nova:

Com isso o Rails carregará menos mediadores: por exemplo, não há suporte a cookies e sessões. Além disso, se tentarmos usar o gerador de esqueleto, visões e ativos não serão criados. Na verdade, se virmos a pasta views/layouts, notaremos que application.html.erb também não existe.

Outra diferença importante é que ApplicationController herda de ActionController::API, não de ActionController::Base.

Fora isso, é uma aplicação Rails como qualquer outra já vista antes. Agora, adicionemos alguns modelos para termos algo com que trabalhar:

Nada demais: um publicação com título e corpo, pertencendo a um usuário.

Garantamos que as associações apropriadas estão configurados e também provenhamos verificações simples:

models/user.rb

models/post.rb

Brilhante! O próximo passo é carregar alguns registros de exemplo nas tabelas recém criadas.

Carregando Dados Demo

A forma mais fácil é utilizando o arquivo seeds.rb dentro do direitório db. Contudo, somos preguiçosos (como outros programadores) e não queremos pensar em conteúdo exemplo. Assim, por que não usar da gem faker, que produz dados aleatórios dos mais vários tipos: nomes, emails, palavras hispsters, textos "lorem ipsum" e mais.

Gemfile

Instalemos a gem:

Agora, alteremos seeds.rb:

db/seeds.rb

Por último, carreguemos os dados:

Respondendo Com JSON

Agora, claro, precisamos de algumas rotas e controladores para a API. É prática comum aninhar as rotas da API dentro do caminho api/. Além disso, desenvolvedores proveem versionamento de APIs, como api/v1/. Depois, se alguma mudança problemática for introduzida, podemos criar um novo espaço de nome (v2) e um novo controlador.

Eis como as rotas podem parecer:

config/routes.rb

Isso gera rotas assim:

Podemos usar o método scope ao invés de namespace, mas, por padrão, ele buscará o UsersController e PostsController dentro da pasta controllers, não dentro de controllers/api/v1, então cuidado.

Criemos uma pasta api com a pasta v1 aninhada, dentro de controllers. Populemos com os controladores:

controllers/api/v1/users_controller.rb

controllers/api/v1/posts_controller.rb

Notemos que temos de, além de aninhar no caminho api/v1, mas a classe em si precisa ter o espaço de nome dentro dos módulos Api e V1.

A próxima questão é: como responder apropriadamente com da dados JSON formatados? Nesse artigo, tentaremos essas soluções: as gems jBuilder e active_model_serializers. Antes de seguir para a próxima seção, coloquemo-nos no Gemfile:

Gemfile

Então executemos:

Usando a Gem jBuilder

jBuilder é uma gem popular mantida pela equipe do Rails que provê uma DSL simples (domain-specific language), permitindo-nos definir estruturas JSON nas visões.

Suponhamos que queremos mostrar todas as publicações quando o usuário acessar a ação index:

controllers/api/v1/posts_controller.rb

Tudo que precisamos é criar uma visão com o nome da ação correspondente, com a extensão .json.jbuilder. Notemos que a visão deve estar no caminho api/v1 também:

views/api/v1/posts/index.json.jbuilder

json.array! percorre o vetor @posts. json.id, json.title e json.body geram as chaves com os nomes correspondentes, usando argumentos como valor. Se navegarmos até http://localhost:3000/api/v1/posts.json, veremos um retorno equivalente a esse:

E se quisermos mostrar o autor de cada publicação? É simples:

O retorno mudará para:

O conteúdo do arquivo .jbuilder é puro Ruby, então podemos usar todas as operações básicas tradicionais.

Notemos que jBuilder suporta parciais como qualquer outra visão Rails, então podemos dizer:

e criar o arquivo views/api/v1/posts/_post.json.jbuilder com o conteúdo a seguir:

Assim, como vemos, jBuilder é fácil e conveniente. Contudo, como alternativa, podemos usar serializadores, então discutamo-nos na próxima seção.

Usando Serializadores

A gem rails_model_serializers foi criada pela equipe que antes administrava a rails-api. Como dito na documentação, rails_model_serializers trás convenção acima de configuração para geração de JSON. Basicamente, definimos que campos devem ser usados na serialização (isso, geração do JSON).

Eis nosso primeiro serializador:

serializers/post_serializer.rb

Aqui dizemos que todos os campos estarão presentes no JSON final. Agora, to_json ou as_json quando invocados numa publicação, usarão essa configuração o retorno correto.

Para vermos em ação, modifiquemos a ação index, assim:

controllers/api/v1/posts_controller.rb

as_json chamará automaticamente no objeto @posts.

E os usuários? Serializadores permitem-nos indicar relações, como modelos. E eles também podem ser aninhados:

serializers/post_serializer.rb

Agora, ao serializar uma publicação, ela conterá a chave user com o id e nome. Se depois criarmos uma serializador separado para o usuário sem o atributo :id:

serializers/post_serializer.rb

então @user.as_json não retornará o id do usuário. Todavia, @post.as_json retornará tanto o nome quanto o id do usuário, então lembre-se.

Protegendo a API

Várias vezes, não queremos alguém realizando qualquer ação na API. Então criemos uma verificação de segurança e forcemos os usuários a enviar seus tokens ao criar e remover publicação.

O token terá um tempo indeterminado e será criado no registro do usuário. Primeiro, adicionemos uma coluna token à tabela users:

Esse índice deve garantir unicidade já que não podemos ter dois usuários com o mesmo token:

db/migrate/xyz_add_token_to_users.rb

Apliquemos a migração:

Agora, adicionemos a callback before_save:

models/user.rb

O método privado generate_token criará um toke em um ciclo infinito e verificará se é único ou não. tão logo o token único é achado, retornamo-no:

models/user.rb

Podemos usar outro algoritmo para a geração do toke, por exemplo, baseado num hash MD5 do nome do usuário e algum sal.

Registro de Usuários

Claro, também precisamos permitir usuários se registrarem ou não poderão obter seus tokens. Não queremos introduzir quaisquer visões HTML à aplicação, então, adicionemos um novo método à API:

controllers/api/v1/users_controller.rb

É uma boa ideia retornar códigos de estado HTTP significativos para desenvolvedores entenderem o que se passa. Agora, podemos prover um novo serializador para os usuários ou continuar com o .json.jbuilder. Preferimos o último (por isso não passamos a opção :json para render), mas somos livres para escolher qualquer um. Notemos, contudo, que o token não deve ser sempre serializado, por exemplo, quando retornarmos uma lista de todos os usuários—deve ficar fora e seguro!

views/api/v1/users/create.json.jbuilder

O próximo passo é testar se tudo funciona. Podemos usar cURL ou criar um código em Ruby. Já que usamos Ruby aqui, vamos com a segunda opção.

Testando o Registro de Usuários

Para realizar uma requisição HTTP, usaremos a gem Faraday, que provê uma interface comum para vários adaptadores (o padrão é Net::HTTP). Criemos um arquivo Ruby separado, incluindo Faraday, e configuremos o cliente:

api_client.rb

Todas as opções são auto-explicativas: escolher o adaptador padrão, configuramos a URL da requisição para http://locahost:3000/api/v1/users, mudamos o tipo do conteúdo para application/json e provemos o corpo da requisição.

A resposta do servidor conterá JSON, então analisemo-no com a gem Oj:

api_client.rb

Além da resposta analisada, também mostramos o código de estado para possível depuração.

Agora, podemos apenas executar o script:

e salvar o token recebido em algum lugar—usaremo-no na próxima seção.

Autenticação Com o Token

Para forçar autenticação com toke, o método authenticate_or_request_with_http_token pode ser usado. Faz parte do módulo ActionController::HttpAuthentication::Token::ControllerMethods então não esqueçamos de incluí-lo:

controllers/api/v1/posts_controller.rb

Adicionemos before_action e o método correspondente:

controllers/api/v1/posts_controller.rb

Agora, se o token não for configurado ou se o usuário com o token não for encontrado, um erro 401 será retornado, parando a execução da ação.

Notemos que a comunicação entre o cliente e o servidor tem de ser via HTTPS, ou os tokens podem ser falsificados. Claro, a solução provida não é ideal, e em vários casos é preferível aplicar o protocolo OAuth 2 para autenticação. Há, pelo menos, duas gems para simplificar bastante o processo de suportar esse recurso: Doorkeeper e oPRO.

Criando uma Publicação

Para ver a autenticação em ação, adicionemos a ação create a PostsController:

controllers/api/v1/posts_controller.rb

Lançamos mão do serializador aqui para mostrar o JSON apropriado. O @user já foi configurado dentro de before_action.

Agora, testemos tudo usando esse código abaixo:

api_client.rb

Substituamos o argumento passado para token_auth com o token recebido no registro e executemos o script.

Apagando uma Publicação

Apagar uma publicação é da mesma forma. Adicionemos a ação destroy:

controllers/api/v1/posts_controller.rb

Só permitimos usuários apagarem publicação que criaram. Se foi removida com sucesso, retornarmos o código de estado 204 (sem conteúdo). Também podemos, responder com o id da publicação apagada, já que continuará disponível na memória.

Eis o código que testará esse novo recurso:

api_client.rb

Mudemos o id da publicação com aquele que servir para nós.

Configurando CORS

Para permitir que outros serviços web acessem nossa API (via lado cliente), é preciso configurar CORS (Cross-Origin Resource Sharing) apropriadamente. Basicamente, CORS permite que aplicações web enviem requisições AJAX a serviços de terceiros. Felizmente, há uma gem chamada rack-cors que nos permite configurar tudo facilmente. Adicionemo-na ao Gemfile:

Gemfile

Instalemo-na:

E forneçamos a configuração dentro do arquivo config/initializers/cors.rb. Na verdade, já foi criado para nós e contém um exemplo de uso. Também encontramos uma documentação bem detalhada na página da gem.

A configuração abaixo, por exemplo, permitirá acesso à API por qualquer um usando qualquer método:

config/initializers/cors.rb

Prevenindo Abusos

A última coisa que mencionaremos nesse guia é como proteger a API de abusos e ataques de negação de serviço. Há uma gem muito legal, rack-attack (criada pelo Kickstarte) que nos permite colocar clientes na lista negra ou branca, prevenindo a inundação de requisições a um servidor e mais.

Coloquemos a gem no Gemfile:

Gemfile

Instalemo-na:

E fornecemos a configuração dentro do arquivo inicializador rack_attack.rb. A documentação da gem lista todas as opções disponíveis e sugere alguns casos de uso. Eis uma configuração exemplo que restringe qualquer um exceto nós de acessar o serviço e limita o número de requisições a 5 por segundo:

config/initializers/rack_attack.rb

Outra coisa que precisamos fazer é incluir RackAttack como mediador:

config/application.rb

Conclusão

Chegamos ao fim do artigo. Esperamos que se sinta confiante em criar APIs com Rails! Vale lembrar que essa não é a única opção—outra solução popular que existe há um tempo é o framework Grape, então vale verificá-lo também.

Não hesitem em deixar suas dúvidas se algo não estiver claro. Agradecemos por lerem tudo e ótima programação!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.