Skip to content

Commit

Permalink
Update [PT] Plug section based on the most recent [EN] version. (elix…
Browse files Browse the repository at this point in the history
  • Loading branch information
rodrigopinto authored and doomspork committed Aug 27, 2017
1 parent 9fee80f commit afae8a3
Showing 1 changed file with 185 additions and 64 deletions.
249 changes: 185 additions & 64 deletions pt/lessons/specifics/plug.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,183 @@
---
version: 0.9.0
version: 1.1.1
title: Plug
---

Se você estiver familiarizado com Ruby, você pode pensar sobre Plug como o Rack com uma pitada de Sinatra, ele fornece uma especificação para componentes de aplicação web e adaptadores para servidores web. Mesmo não fazendo parte do núcleo de Elixir, Plug é um projeto oficial de Elixir.
Se você estiver familiarizado com Ruby, você pode pensar sobre Plug como o Rack com uma pitada de Sinatra. Ele fornece uma especificação para componentes de aplicação web e adaptadores para servidores web. Mesmo não fazendo parte do núcleo de Elixir, Plug é um projeto oficial de Elixir.

Nós iremos começar criando uma mini aplicação web baseada no Plug. Depois, iremos aprender sobre as rotas do Plug e como adicionar um Plug à uma aplicação web existente.

{% include toc.html %}

## Instalação
## Pré-requisitos

Este tutorial assume que você já tenha Elixir 1.4 ou superior e o `mix` instalados.

Se você não tem um projeto iniciado, crie um:

```shell
mix new example
cd example
```

## Dependências

A instalação é uma brisa se você utilizar mix. Para instalar Plug nós precisamos fazer duas pequenas alterações no nosso `mix.exs`. A primeira coisa a fazer é adicionar tanto Plug quanto um servidor web para o nosso arquivo de dependências, vamos utilizar Cowboy:
Adicionar dependências é uma facilidade com mix. Para instalar Plug nós precisamos fazer duas pequenas alterações no nosso `mix.exs`.
A primeira coisa a fazer é adicionar tanto Plug quanto um servidor web(vamos utilizar o Cowboy) no nosso arquivo de dependências:

```elixir
defp deps do
[{:cowboy, "~> 1.1.2"},
{:plug, "~> 1.3.4"}]
[
{:cowboy, "~> 1.1.2"},
{:plug, "~> 1.3.4"},
]
end
```

A última coisa que nós precisamos fazer é adicionar tanto o nosso servidor web quanto o Plug na nossa aplicação OTP:
No terminal, rode o seguinte comando mix para baixar as novas dependências:

```elixir
def application do
[applications: [:cowboy, :logger, :plug]]
end
```shell
$ mix deps.get
```

## A especificação

A fim de começar a criar Plugs, nós precisamos saber e aderir a especificação Plug. Felizmente para nós, existem apenas duas funções necessárias: `init/1` e `call/2`.

A função `init/1` é usada para iniciar as opções do nosso Plug, passando como o segundo argumento para nossa função `call/2`. Além de nossas opções para inicialização da função `call/2`, a função recebe um `%Plug.Conn` como seu primeiro argumento, e é esperado que isto retorne uma conexão.
A fim de começar a criar Plugs, nós precisamos saber e aderir a especificação Plug.
Felizmente para nós, existem apenas duas funções necessárias: `init/1` e `call/2`.

Aqui está um simples plug que retorna "Olá mundo!":
Aqui está um Plug simples que retorna "Hello World!":

```elixir
defmodule HelloWorldPlug do
defmodule Example.HelloWorldPlug do
import Plug.Conn

def init(options), do: options

def call(conn, _opts) do
conn
|> put_resp_content_type("text/plain")
|> send_resp(200, "Hello World!")
|> send_resp(200, "Hello World!\n")
end
end
```

Salve o arquivo como `lib/example/hello_world_plug.ex`.

A função `init/1` é usada para iniciar as opções do nosso Plug. Ele é chamado pela árvore de supervisores, que é explicado na próxima seção. Por agora, este será uma Lista vazia que será ignorada.

O valor retornado do `init/1` será eventualmente passado para `call/2` como segundo argumento.

A função `call/2` é chamada para cada nova requisição recebida pelo servidor web Cowboy.
Ela recebe um `%Plug.Conn{}` struct como seu primeiro argumento, e é esperado que isto retorne um struct `%Plug.Conn{}`.

## Configurando o módulo do projeto

Dado que estamos criando uma aplicação Plug do zero, precisamos definir o módulo da aplicação.
Atualize `lib/example.ex` para iniciar e superviosionar o Cowboy.

```elixir
defmodule Example do
use Application
require Logger

def start(_type, _args) do
children = [
Plug.Adapters.Cowboy.child_spec(:http, Example.HelloWorldPlug, [], port: 8080)
]

Logger.info "Started application"

Supervisor.start_link(children, strategy: :one_for_one)
end
end
```

## Criando um Plug
Ele supervisiona o Cowboy, que por sua vez, supervisiona nosso `HelloWorldPlug`.

Na chamada `Plug.Adapters.Cowboy.child_spec/4`, o terceiro argumento será passado para `Example.HelloWorldPlug.init/1`.

Nós não terminamos ainda. Abra o `mix.exs` novamente, e encontre o método `applications`.
Precisamos adicionar uma configuração para nossa aplicação, o que fará com que ele inicie automaticamente.

Vamos atualizar para fazer isso:

```elixir
def application do
[
extra_applications: [:logger],
mod: {Example, []}
]
end
```

Estamos prontos para testar este servidor web minimalista, baseado no Plug.
No terminal, execute:

```shell
$ mix run --no-halt
```

Quando a compilação estiver terminado, e aparecer `[info] Started app`, abra o navegador em `localhost:8080`. Ele deve exibir:

```
Hello World!
```

## Plug.Router

Para a maioria das aplicações, como um site web ou uma API REST, você irá querer um router para orquestrar as requisições de diferentes paths e verbos HTTP, para diferentes manipuladores. `Plug` fornece um router para fazer isso. Como veremos, não precisamos de um framework como Sinatra em Elixir dado que nós temos isso de graça no Plug.

Para começar, vamos criar um arquivo `lib/example/router.ex` e colar o trecho a seguir nele:

```elixir
defmodule Example.Router do
use Plug.Router

plug :match
plug :dispatch

get "/", do: send_resp(conn, 200, "Welcome")
match _, do: send_resp(conn, 404, "Oops!")
end
```

Este é um mini Router, mas o código deve ser bastante auto-explicativo.
Nós incluímos alguns macros através de `use Plug.Router`, e em seguida, configuramos dois Plugs nativos: `:match` e `:dispatch`.
Existem duas rotas definidas, uma para mapear requisições GET para a raiz e a segunda para mapear todos as outras requisições, e então possamos retornar uma mensagem 404.

De volta ao `lib/example.ex`, precisamos adicionar o `Example.Router` na árvode de supervisores.
Troque o `Example.HelloWorldPlug` plug para o novo router:

```elixir
def start(_type, _args) do
children = [
Plug.Adapters.Cowboy.child_spec(:http, Example.Router, [], port: 8080)
]
Logger.info "Started application"
Supervisor.start_link(children, strategy: :one_for_one)
end
```

Reinicie o servidor, pare o anterior se ele estiver rodando (pressione duas vezes `Ctrl+C`).

Agora no navegador, digite `localhost:8080`.
Você deve ver `Welcome`.
Então, digite `localhost:8080/waldo`, ou qualquer outro path. Isto deve retornar `Oops!` com uma resposta 404.

## Adicionando outro Plug

Para este exemplo, iremos criar um Plug para verificar se a requisição tem um conjunto de parâmetros necessários. Ao implementar a nossa validação em um Plug, podemos ter a certeza de que apenas os pedidos válidos serão gerenciados através de nosso aplicativo. Vamos esperar que o nosso Plug seja inicializado com duas opções: `:paths` e `:fields`. Estes irão representar os caminhos que aplicamos nossa lógica onde os campos são exigidos.
É comum criar Plugs para interceptar todas as requisições ou um subconjunto delas, para manipular lógicas comuns às requisições.

_Note_: Plugs são aplicadas a todas as requisições, e é por isso que nós iremos lidar com filtragem de solicitações e aplicar nossa lógica para apenas um subconjunto deles. Para ignorar um pedido simplesmente passamos a conexão através do mesmo.
Para este exemplo, iremos criar um Plug para verificar se a requisição tem um conjunto de parâmetros necessários. Ao implementar a nossa validação em um Plug, podemos ter a certeza de que apenas as requisições válidas serão processadas pela nossa aplicação.
Vamos esperar que o nosso Plug seja inicializado com duas opções: `:paths` e `:fields`. Estes irão representar os caminhos que aplicamos nossa lógica, e onde os campos são exigidos.

Vamos começar analisando o Plug que acabamos de concluir, e em seguida, discutir como ele funciona, vamos criá-o em `lib/plug/verify_request.ex`:
_Note_: Plugs são aplicados a todas as requisições, e é por isso que nós filtraremos as requisições e aplicararemos nossa lógica para apenas um subconjunto delas.
Para ignorar uma requisição simplesmente passamos a conexão através do mesmo.

Vamos começar analisando o Plug que acabamos de concluir, e em seguida, discutir como ele funciona, vamos criá-lo em `lib/plug/verify_request.ex`:

```elixir
defmodule Example.Plug.VerifyRequest do
import Plug.Conn

defmodule IncompleteRequestError do
@moduledoc """
Expand All @@ -85,72 +204,59 @@ defmodule Example.Plug.VerifyRequest do
defp contains_fields?(keys, fields), do: Enum.all?(fields, &(&1 in keys))
end
```
A primeira coisa a ser notada é que definimos uma nova exceção `IncompleteRequestError` e que uma de suas opções é `:plug_status`. Quando disponível esta opção é usada pelo Plug para definir o código de status do HTTP no caso de uma exceção.

A segunda parte do nosso Plug é a função `call/2`, este é o lugar onde nós lidamos quando aplicar ou não nossa lógica de verificação. Somente quando o caminho do pedido está contido em nossa opção `:paths` iremos chamar `verify_request/2`.

A última parte do nosso Plug é a função privada `verify_request!/2` no qual verifica quando os campos requeridos `:fields` estão todos presentes. No caso em que algum dos campos requeridos estiver em falta, nós acionamos `IncompleteRequestError`.

## Usando Plug.Router

Agora que temos o nosso Plug `VerifyRequest`, podemos seguir para o nosso roteador. Como estamos prestes a ver, não precisamos de uma estrutura como Sinatra em Elixir, ganhamos isso de graça com Plug.

Para iniciar vamos criar um arquivo `lib/plug/router.ex` e copiar o seguinte código dentro deste:
A primeira coisa a ser notada é que definimos uma nova exceção `IncompleteRequestError` e que uma de suas opções é `:plug_status`. Quando disponível esta opção é usada pelo Plug para definir o código do status HTTP no caso de uma exceção.

```elixir
defmodule Example.Plug.Router do
use Plug.Router
A segunda parte do nosso Plug é a função `call/2`. Este é o lugar onde nós lidamos quando aplicar ou não nossa lógica de verificação. Somente quando o path da requisição está contido em nossa opção `:paths` iremos chamar `verify_request/2`.

plug :match
plug :dispatch
A última parte do nosso Plug é a função privada `verify_request!/2` no qual verifica se os campos requeridos `:fields` estão todos presentes. No caso em que algum dos campos requeridos estiver em falta, nós acionamos `IncompleteRequestError`.

get "/", do: send_resp(conn, 200, "Welcome")
match _, do: send_resp(conn, 404, "Oops!")
end
```

Este é um Router mínimo, mas o código deve ser bastante auto-explicativo. Nós incluímos alguns macros através de `use Plug.Router`, e em seguida, configuramos dois Plugs nativos: `:match` e `:dispatch`. Existem duas rotas definidas, uma para manipulação retornos de GET para a raiz e a segunda para combinar todos os outros requests para que possamos retornar uma mensagem 404.
Configuramos o nosso Plug para verificar se todas as requisições para `/upload` incluem tanto `"content"` quanto `"mimetype"`, só então o código da rota irá ser executado.

Vamos adicionar o nosso plug ao roteador:
Agora, precisamos notificar o roteador sobre o novo Plug.
Edite o `lib/example/router.ex` e adicione as seguintes mudanças:

```elixir
defmodule Example.Plug.Router do
defmodule Example.Router do
use Plug.Router

alias Example.Plug.VerifyRequest

plug Plug.Parsers, parsers: [:urlencoded, :multipart]
plug VerifyRequest, fields: ["content", "mimetype"],
paths: ["/upload"]

plug :match
plug :dispatch

get "/", do: send_resp(conn, 200, "Welcome")
post "/upload", do: send_resp(conn, 201, "Uploaded")
match _, do: send_resp(conn, 404, "Oops!")
get "/", do: send_resp(conn, 200, "Welcome\n")
post "/upload", do: send_resp(conn, 201, "Uploaded\n")
match _, do: send_resp(conn, 404, "Oops!\n")
end
```
É isso aí! Nós configuramos o nosso Plug para verificar se todas as requisições para `/upload` incluem tanto `"content"` quanto `"mimetype"`, só então o código de rota irá ser executado.

Por agora nosso endpoint `/upload` não é muito útil, mas vimos como criar e integrar o nosso Plug.

## Deixando a porta HTTP Configurável

## Executando nosso Web App
Quando definimos a aplicação e o módulo `Example`, a porta HTTP foi definida diretamente no código do módulo.
É considerado uma boa prática, deixar a porta configurável usando um arquivo de configuração.

Antes de podermos executar nossa aplicação nós precisamos instalar e configurar o nosso servidor web, neste caso Cowboy. Por agora vamos fazer as mudanças necessários no código para rodar tudo, e então vamos nos aprofundar em coisas específicas em lições futuras.

Vamos começar por atualizar parte de `application` do nosso `mix.exs` para especificar ao Elixir sobre a aplicação e variáveis de ambiente. Com essas alterações nosso código local deve parecer com isso:
Vamos começar por atualizar o método `application` do nosso `mix.exs` para especificar ao Elixir sobre a aplicação e variáveis de ambiente. Com essas alterações nosso código local deve parecer com isso:


```elixir
def application do
[applications: [:cowboy, :plug],
mod: {Example, []},
env: [cowboy_port: 8080]]
[
extra_applications: [:logger],
mod: {Example, []},
env: [cowboy_port: 8080]
]
end
```

Em seguida, precisamos atualizar `lib/example.ex` para iniciar o supervisor para o Cowboy:
Nossa aplicação está configurada na linha `mod: {Example, []}`.
Observe que também estamos inicializando as aplicações `cowboy`, `logger` e `plug`.

Em seguida, precisamos atualizar `lib/example.ex` para ler a porta do arquivo de configuração, e passar para o Cowboy:

```elixir
defmodule Example do
Expand All @@ -168,6 +274,16 @@ defmodule Example do
end
```

O terceiro argumento do `Application.get_env` é um valor padrão para quando a variável de configuração não estiver definida.

> (Optional) add `:cowboy_port` in `config/config.exs`
```elixir
use Mix.Config

config :example, cowboy_port: 8080
```

Agora para executar nossa aplicação, podemos usar:

```shell
Expand All @@ -178,15 +294,14 @@ $ mix run --no-halt

Testes em Plugs são bastante simples, graças ao `Plug.Test`, que inclui uma série de funções convenientes para fazer o teste ser algo fácil.

Veja se você consegue seguir o raciocínio do teste no router:

Escreva o código de teste a seguir em `test/example/router_test.exs`:

```elixir
defmodule RouterTest do
defmodule Example.RouterTest do
use ExUnit.Case
use Plug.Test

alias Example.Plug.Router
alias Example.Router

@content "<html><body>Hi!</body></html>"
@mimetype "text/html"
Expand Down Expand Up @@ -220,6 +335,12 @@ defmodule RouterTest do
end
```

Execute com o comando:

```shell
mix test test/example/router_test.exs
```

## Plugs disponíveis

Há um número de Plugs disponíveis e fáceis de utilizar, a lista completa pode ser encontrada na documentação do Plug [neste link](https://github.com/elixir-lang/plug#available-plugs).
Existem inúmeros Plugs disponíveis e fáceis de utilizar, a lista completa pode ser encontrada na documentação do Plug [neste link](https://github.com/elixir-lang/plug#available-plugs).

0 comments on commit afae8a3

Please sign in to comment.