Π ΡΡΠΎΠΌ ΠΏΠΎΡΡΠ΅ ΠΌΡ ΡΠ·Π½Π°Π΅ΠΌ, ΠΊΠ°ΠΊ ΠΏΠΎΡΡΠ΅Π±Π»ΡΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· Π²Π½Π΅ΡΠ½Π΅Π³ΠΎ API. ΠΠΎ Π²ΡΠ΅ΠΌΡ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΌΡ ΡΠ΄Π΅Π»Π°Π΅ΠΌ Π²ΡΠ·ΠΎΠ² ViaCep API Π΄Π»Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ Π΅Π³ΠΎ ΠΏΠΎΡΡΠΎΠ²ΠΎΠ³ΠΎ ΠΈΠ½Π΄Π΅ΠΊΡΠ°, ΠΏΠΎΠ»ΡΡΠΈΠΌ ΠΈ ΡΠΎΡ ΡΠ°Π½ΠΈΠΌ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ Π³ΠΎΡΠΎΠ΄Π΅ ΠΈ UF ΡΡΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ. ΠΡ ΡΠ°ΠΊΠΆΠ΅ ΡΠ·Π½Π°Π΅ΠΌ, ΠΊΠ°ΠΊ ΠΈΠ·Π±Π΅ΠΆΠ°ΡΡ Π½Π΅Π½ΡΠΆΠ½ΡΡ Π·Π²ΠΎΠ½ΠΊΠΎΠ².
- Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΡΠΎΠ΅ΠΊΡΠ°
- Π’Π°Π±Π»ΠΈΡΠ° ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Π΅ΠΉ
- Π€ΡΠ½ΠΊΡΠΈΡ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ
- ΠΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ
ΠΈΠ· Π²Π½Π΅ΡΠ½Π΅Π³ΠΎ API
- Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Tesla
- Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° HTTPoison
- HTTP-ΠΊΠ»ΠΈΠ΅Π½Ρ Ρ Tesla
- HTTP-ΠΊΠ»ΠΈΠ΅Π½Ρ Ρ HTTPoison
- ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΠΏΠΎΡΡΠΎΠ²ΠΎΠ³ΠΎ ΠΈΠ½Π΄Π΅ΠΊΡΠ° ΠΏΡΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ
- ΠΠ·Π±Π΅Π³Π°Π½ΠΈΠ΅ Π½Π΅Π½ΡΠΆΠ½ΡΡ ΠΎΠ±ΡΠ°ΡΠ΅Π½ΠΈΠΉ ΠΊ Π²Π½Π΅ΡΠ½Π΅ΠΌΡ API (apply_action)
- ΠΠ°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ User-Agent
- ΠΠ°ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
- Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΡΠΎΠ΅ΠΊΡΠ°
- ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΠΈ ΡΡΠΎΠ»ΠΎΠ²
- Π€ΡΠ½ΠΊΡΠΈΡ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ
- ΠΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ ΠΈΠ· Π²Π½Π΅ΡΠ½Π΅Π³ΠΎ API
- Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Tesla
- Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° HTTPoison
- HTTP-ΠΊΠ»ΠΈΠ΅Π½Ρ Ρ Tesla
- HTTP-ΠΊΠ»ΠΈΠ΅Π½Ρ Ρ HTTPoison
- ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΠΏΠΎΡΡΠΎΠ²ΠΎΠ³ΠΎ ΠΈΠ½Π΄Π΅ΠΊΡΠ° ΠΏΡΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ
- ΠΠ΅Π²Π΅ΡΠ½ΡΠΉ ΠΏΠΎΡΡΠΎΠ²ΡΠΉ ΠΈΠ½Π΄Π΅ΠΊΡ
- ΠΠ·Π±Π΅Π³Π°Π½ΠΈΠ΅ Π½Π΅Π½ΡΠΆΠ½ΡΡ ΠΎΠ±ΡΠ°ΡΠ΅Π½ΠΈΠΉ ΠΊ Π²Π½Π΅ΡΠ½Π΅ΠΌΡ API (apply_action)
- ΠΠ°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ User-Agent
- ΠΠ°ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΡΠΎΠ΅ΠΊΡΠ°
$ mix phx.new learning_external_api --app my_app
Π‘ΡΡΠ»ΠΊΠ° Π½Π° Π³ΠΎΡΠΎΠ²ΡΠΉ ΠΏΡΠΎΠ΅ΠΊΡ: https://github.com/maiquitome/learning_external_api
ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΠΈ ΡΡΠΎΠ»ΠΎΠ²
Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΌΠΈΠ³ΡΠ°ΡΠΈΠΈ ΠΈ ΡΡ Π΅ΠΌΡ:
$ mix phx.gen.schema User users first_name last_name email cep city uf
ΠΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΡΡ Π΅ΠΌΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ:
Π£Π΄Π°Π»ΠΈΡΠ΅ city
ΠΈ uf
ΡΠΎΠ»ΡΠΊΠΎ ΠΈΠ· validate_required
.
Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π±Π°Π·Ρ Π΄Π°Π½Π½ΡΡ ΠΈ Π·Π°ΠΏΡΡΠΊ ΠΌΠΈΠ³ΡΠ°ΡΠΈΠΉ:
$ mix ecto.setup
Π€ΡΠ½ΠΊΡΠΈΡ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ
ΠΠ°ΠΌ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡΡΡ ΡΡΠ½ΠΊΡΠΈΡ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ, ΠΏΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ViaCEP API Π΄Π»Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΏΠΎΡΡΠΎΠ²ΠΎΠ³ΠΎ ΠΈΠ½Π΄Π΅ΠΊΡΠ° ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ. ΠΡ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΡΡΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ ΠΏΠΎΠ·ΠΆΠ΅.
Π lib/my_app/users/create.ex
:
defmodule MyApp.Users.Create do
alias MyApp.{Repo, User}
@type user_params :: %{
first_name: String.t(),
last_name: integer,
cep: String.t(),
email: String.t()
}
@doc """
Inserts a user into the database.
## Examples
iex> alias MyApp.{User, Users}
...>
...> user_params = %{
...> first_name: "Mike",
...> last_name: "Wazowski",
...> cep: "95270000",
...> email: "mike_wazowski@monstros_sa.com"
...> }
...>
...> {:ok, %User{}} = Users.Create.call user_params
...>
iex> {:error, %Ecto.Changeset{}} = Users.Create.call %{}
"""
@spec call(user_params()) :: {:error, Ecto.Changeset.t()} | {:ok, Ecto.Schema.t()}
def call(params) do
%User{}
|> User.changeset(params)
|> Repo.insert()
end
end
ΠΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ ΠΈΠ· Π²Π½Π΅ΡΠ½Π΅Π³ΠΎ API
Π§ΡΠΎΠ±Ρ ΠΈΠΌΠ΅ΡΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡ ΠΏΠΎΡΡΠ΅Π±Π»ΡΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· Π²Π½Π΅ΡΠ½Π΅Π³ΠΎ API, Π½Π°ΠΌ Π½ΡΠΆΠ΅Π½ HTTP-ΠΊΠ»ΠΈΠ΅Π½Ρ. ΠΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π»ΠΈΠ±ΠΎ Tesla, Π»ΠΈΠ±ΠΎ HTTPoison. ΠΡΠ΅ΠΈΠΌΡΡΠ΅ΡΡΠ²ΠΎ Tesla Π² ΡΠΎΠΌ, ΡΡΠΎ Ρ Π½Π΅Π΅ Π΅ΡΡΡ Π³ΠΎΡΠΎΠ²ΠΎΠ΅ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΠΎΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ½ΠΎΠ΅ ΠΎΠ±Π΅ΡΠΏΠ΅ΡΠ΅Π½ΠΈΠ΅, ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ. Π ΡΡΠΎΠΌ ΠΏΠΎΡΡΠ΅ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΎΠ±Π° ΠΊΠ»ΠΈΠ΅Π½ΡΠ° Π΄Π»Ρ ΠΈΡ ΡΡΠ°Π²Π½Π΅Π½ΠΈΡ.
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Tesla
Π§ΡΠΎΠ±Ρ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ Tesla, Π΄ΠΎΠ±Π°Π²ΡΡΠ΅ Π² mix.exs
:
defp deps do
[
{:tesla, "~> 1.4"},
# opcional, mas recomendado
{:hackney, "~> 1.17"}
# esse nΓ£o precisa pois jΓ‘ vem com o phoenix
{:jason, ">= 1.0.0"}
]
end
ΠΠΎΡΠ»Π΅Π΄Π½ΡΡ Π²Π΅ΡΡΠΈΡ Tesla ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΉΡΠΈ ΠΏΠΎ Π°Π΄ΡΠ΅ΡΡ: https://hex.pm/packages/tesla.
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ:
$ mix deps.get
ΠΡΠΏΡΡΠ°Π½ΠΈΠ΅ Tesla:
$ iex -S mix
iex> Tesla.get "https://viacep.com.br/ws/01001000/json/"
{:ok, %Tesla.Env{}}
iex> Tesla.get ""
{:error, {:no_scheme}}
iex> Tesla.get "https://exemplo.com"
{:error, :econnrefused}
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° HTTPoison
Π§ΡΠΎΠ±Ρ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ HTTPoison, Π΄ΠΎΠ±Π°Π²ΡΡΠ΅ Π² mix.exs
:
defp deps do
[
{:httpoison, "~> 1.8"}
]
end
ΠΡ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π½Π°ΠΉΡΠΈ ΠΏΠΎΡΠ»Π΅Π΄Π½ΡΡ Π²Π΅ΡΡΠΈΡ HTTPoison ΠΏΠΎ Π°Π΄ΡΠ΅ΡΡ: https://hex.pm/packages/httpoison.
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ:
$ mix deps.get
Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ HTTPoison:
$ iex -S mix
iex> HTTPoison.get "https://viacep.com.br/ws/01001000/json/"
{:ok, %HTTPoison.Response{}}
iex> HTTPoison.get ""
** (CaseClauseError) no case clause matching: []
iex> HTTPoison.get "https://exemplo.com"
{:error, %HTTPoison.Error{id: nil, reason: :closed}}
HTTP-ΠΊΠ»ΠΈΠ΅Π½Ρ Ρ Tesla
ΠΠ°Π²Π°ΠΉΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΈΠΌΠ΅Π½ΡΠ΅ΠΌ ΡΠ°ΠΉΠ» tesla_client.ex
, ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ ΠΌΡ ΡΠΎΠ·Π΄Π°Π΄ΠΈΠΌ Π΄ΡΡΠ³ΠΎΠΉ ΡΠ°ΠΉΠ» ΠΏΠΎΠ΄ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ httpoison_client.ex
, ΠΈ ΡΠ°ΠΊΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ ΠΌΡ ΡΠΌΠΎΠΆΠ΅ΠΌ ΡΡΠ°Π²Π½ΠΈΠ²Π°ΡΡ HTTP-ΠΊΠ»ΠΈΠ΅Π½ΡΡ
Π»ΡΡΡΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ.
Π lib/my_app/via_cep/tesla_client.ex
:
defmodule MyApp.ViaCep.TeslaClient do
# Ao invΓ©s de Tesla.get(), vc vai usar apenas get()
use Tesla
alias Tesla.Env
@base_url "https://viacep.com.br/ws/"
# codifica (encode) os parametros para json
# e descodifica (decode) a resposta para json automaticamente.
plug Tesla.Middleware.JSON
def get_cep_info(url \ @base_url, cep) do
"#{url}#{cep}/json/"
|> get()
|> handle_get()
end
# casos abaixo de sucesso e erro
defp handle_get({:ok, %Env{status: 200, body: %{"erro" => "true"}}}) do
{:error, %{status: :not_found, result: "CEP not found!"}}
end
defp handle_get({:ok, %Env{status: 200, body: body}}) do
{:ok, body}
end
defp handle_get({:ok, %Env{status: 400, body: _body}}) do
{:error, %{status: :bad_request, result: "Invalid CEP!"}}
end
defp handle_get({:error, reason}) do
{:error, %{status: :bad_request, result: reason}}
end
end
ΠΠ°Π²Π°ΠΉΡΠ΅ Π·Π°ΠΏΡΡΡΠΈΠΌ ΡΠ΅ΡΡ:
iex(1)> MyApp.ViaCep.TeslaClient.get_cep_info "95270000"
{:ok,
%{
"bairro" => "",
"cep" => "95270-000",
"complemento" => "",
"ddd" => "54",
"gia" => "",
"ibge" => "4308201",
"localidade" => "Flores da Cunha",
"logradouro" => "",
"siafi" => "8661",
"uf" => "RS"
}}
iex(2)> MyApp.ViaCep.TeslaClient.get_cep_info "95270001"
{:error, %{result: "CEP not found!", status: :not_found}}
iex(3)> MyApp.ViaCep.TeslaClient.get_cep_info ""
{:error, %{result: "Invalid CEP!", status: :bad_request}}
HTTP-ΠΊΠ»ΠΈΠ΅Π½Ρ Ρ HTTPoison
Π HTTPoison Ρ Π½Π°Ρ Π½Π΅Ρ Π³ΠΎΡΠΎΠ²ΠΎΠ³ΠΎ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΠΎΠ³ΠΎ ΠΠ Π΄Π»Ρ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΠΎΠ²Π°Π½ΠΈΡ json
Π² map
, ΠΏΠΎΡΡΠΎΠΌΡ Π½Π°ΠΌ ΠΏΡΠΈΠ΄Π΅ΡΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Jason.decode()
, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠΆΠ΅ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ Π² Phoenix.
Π lib/my_app/via_cep/httpoison_client.ex
:
defmodule MyApp.ViaCep.HttpoisonClient do
alias HTTPoison.{Error, Response}
@base_url "https://viacep.com.br/ws/"
def get_cep_info(url \ @base_url, cep) do
"#{url}#{cep}/json/"
|> HTTPoison.get()
|> json_to_map()
|> handle_get()
end
defp json_to_map({:ok, %Response{body: body} = response}) do
{_ok_or_error, body} = Jason.decode(body)
{:ok, Map.put(response, :body, body)}
end
defp json_to_map({:error, %Error{}} = error), do: error
defp handle_get({:ok, %Response{status_code: 200, body: %{"erro" => "true"}}}) do
{:error, %{status: :not_found, result: "CEP not found!"}}
end
defp handle_get({:ok, %Response{status_code: 200, body: body}}) do
{:ok, body}
end
defp handle_get({:ok, %Response{status_code: 400, body: _body}}) do
{:error, %{status: :bad_request, result: "Invalid CEP!"}}
end
defp handle_get({:error, reason}) do
{:error, %{status: :bad_request, result: reason}}
end
end
ΠΠ°Π²Π°ΠΉΡΠ΅ Π·Π°ΠΏΡΡΡΠΈΠΌ ΡΠ΅ΡΡ:
iex(1)> MyApp.ViaCep.HttpoisonClient.get_cep_info "95270000"
{:ok,
%{
"bairro" => "",
"cep" => "95270-000",
"complemento" => "",
"ddd" => "54",
"gia" => "",
"ibge" => "4308201",
"localidade" => "Flores da Cunha",
"logradouro" => "",
"siafi" => "8661",
"uf" => "RS"
}}
iex(2)> MyApp.ViaCep.HttpoisonClient.get_cep_info "95270001"
{:error, %{result: "CEP not found!", status: :not_found}}
iex(3)> MyApp.ViaCep.HttpoisonClient.get_cep_info ""
{:error, %{result: "Invalid CEP!", status: :bad_request}}
ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΠΏΠΎΡΡΠΎΠ²ΠΎΠ³ΠΎ ΠΈΠ½Π΄Π΅ΠΊΡΠ° ΠΏΡΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ
Π lib/my_app/users/create.ex
ΠΈΠ·ΠΌΠ΅Π½ΠΈΠΌ ΡΡΠ½ΠΊΡΠΈΡ Π΄Π»Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΏΠΎΡΡΠΎΠ²ΠΎΠ³ΠΎ ΠΈΠ½Π΄Π΅ΠΊΡΠ° ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ, Π° ΡΠ°ΠΊΠΆΠ΅ Π΄Π»Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎ Π³ΠΎΡΠΎΠ΄Π΅ ΠΈ ΡΡΠ°ΡΠ΅. Π£ ΡΡΠΎΠΉ ΡΡΠ½ΠΊΡΠΈΠΈ Π΅ΡΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ°, Π½ΠΎ ΠΌΡ ΡΠ»ΡΡΡΠΈΠΌ Π΅Π΅ ΠΏΠΎΠ·ΠΆΠ΅.
alias MyApp.ViaCep.HttpoisonClient, as: Client
...
@spec call(user_params()) :: {:error, Ecto.Changeset.t() | map()} | {:ok, Ecto.Schema.t()}
def call(params) do
cep = Map.get(params, :cep)
with {:ok, %{"localidade" => city, "uf" => uf}} <- Client.get_cep_info(cep),
params <- Map.merge(params, %{city: city, uf: uf}),
changeset <- User.changeset(%User{}, params),
{:ok, %User{}} = user <- Repo.insert(changeset) do
user
end
end
Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ:
iex(1)> user_params = %{
...(1)> first_name: "Mike",
...(1)> last_name: "Wazowski",
...(1)> cep: "95270000",
...(1)> email: "mike_wazowski@monstros_sa.com"
...(1)> }
%{
cep: "95270000",
email: "mike_wazowski@monstros_sa.com",
first_name: "Mike",
last_name: "Wazowski"
}
iex(2)> MyApp.Users.Create.call user_params
CEP: "95270000"
{:ok,
%MyApp.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
cep: "95270000",
city: "Flores da Cunha",
email: "mike_wazowski@monstros_sa.com",
first_name: "Mike",
id: 1,
inserted_at: ~N[2022-05-15 21:38:14],
last_name: "Wazowski",
uf: "RS",
updated_at: ~N[2022-05-15 21:38:14]
}}
ΠΠ΅Π²Π΅ΡΠ½ΡΠΉ ΠΏΠΎΡΡΠΎΠ²ΡΠΉ ΠΈΠ½Π΄Π΅ΠΊΡ
iex(1)> user_params = %{
...(1)> first_name: "Mike",
...(1)> last_name: "Wazowski",
...(1)> cep: "123",
...(1)> email: "mike_wazowski@monstros_sa.com"
...(1)> }
%{
cep: "123",
email: "mike_wazowski@monstros_sa.com",
first_name: "Mike",
last_name: "Wazowski"
}
iex(2)> MyApp.Users.Create.call user_params
CEP: "123"
{:error, %{result: "Invalid CEP!", status: :bad_request}}
ΠΠ±ΡΠ°ΡΠΈΡΠ΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, ΡΡΠΎ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ Invalid ZIP Code!
Π±ΡΠ»ΠΎ ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΎ Π²ΠΌΠ΅ΡΡΠΎ ΡΠΎΠ³ΠΎ, ΡΡΠΎΠ±Ρ ΠΏΠΎΠΊΠ°Π·Π°ΡΡ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΡ Π½Π°Π±ΠΎΡΠ° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ, ΠΏΡΠ΅Π΄ΡΠΏΡΠ΅ΠΆΠ΄Π°Ρ, ΡΡΠΎ ZIP Code Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ 8 ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ², ΡΠ°ΠΊΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ, ΠΌΡ ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΈ Π±Ρ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΡ Π±Π΅Π· Π»ΠΈΡΠ½Π΅Π³ΠΎ Π²ΡΠ·ΠΎΠ²Π° Π²Π½Π΅ΡΠ½Π΅Π³ΠΎ API.
ΠΠ·Π±Π΅Π³Π°Π½ΠΈΠ΅ Π½Π΅Π½ΡΠΆΠ½ΡΡ ΠΎΠ±ΡΠ°ΡΠ΅Π½ΠΈΠΉ ΠΊ Π²Π½Π΅ΡΠ½Π΅ΠΌΡ API (apply_action)
Π’Π΅ΠΏΠ΅ΡΡ Π΄Π°Π²Π°ΠΉΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΈΠΌ Π²ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ Π² Π½Π°Π±ΠΎΡΠ΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ ΠΏΠ΅ΡΠ΅Π΄ Π²ΡΠ·ΠΎΠ²ΠΎΠΌ ViaCep API.
Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ Π½Π°Π±ΠΎΡΠ° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Π±Π΅Π· ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ Repo.insert()
:
iex(1)> import Ecto.Changeset
iex(2)> user_params = %{
...(2)> first_name: "Mike",
...(2)> last_name: "Wazowski",
...(2)> cep: "123",
...(2)> email: "mike_wazowski@monstros_sa.com"
...(2)> }
%{
cep: "123",
email: "mike_wazowski@monstros_sa.com",
first_name: "Mike",
last_name: "Wazowski"
}
iex(3)> MyApp.User.changeset(%MyApp.User{}, user_params) |> apply_action(:create)
{:error,
#Ecto.Changeset<
action: :create,
changes: %{
cep: "123",
email: "mike_wazowski@monstros_sa.com",
first_name: "Mike",
last_name: "Wazowski"
},
errors: [
cep: {"should be %{count} character(s)",
[count: 8, validation: :length, kind: :is, type: :string]}
],
data: #MyApp.User<>,
valid?: false
>}
apply_action
ΠΏΡΠΈΠΌΠ΅Π½ΡΠ΅Ρ Π΄Π΅ΠΉΡΡΠ²ΠΈΠ΅ Π½Π°Π±ΠΎΡΠ° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ, ΡΠΎΠ»ΡΠΊΠΎ Π΅ΡΠ»ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½Ρ.
ΠΡΠ»ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½Ρ, Π²ΡΠ΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π±ΡΠ΄ΡΡ ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½Ρ ΠΊ Π΄Π°Π½Π½ΡΠΌ Π½Π°Π±ΠΎΡΠ° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ. ΠΡΠ»ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π½Π΅Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½Ρ, ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π½Π΅ Π±ΡΠ΄ΡΡ ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½Ρ, ΠΈ Π±ΡΠ΄Π΅Ρ Π²ΠΎΠ·Π²ΡΠ°ΡΠ΅Π½ ΠΊΠΎΡΡΠ΅ΠΆ ΠΎΡΠΈΠ±ΠΎΠΊ Ρ Π½Π°Π±ΠΎΡΠΎΠΌ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ, ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΠΈΠΌ Π΄Π΅ΠΉΡΡΠ²ΠΈΠ΅, ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΏΡΡΠ°Π»ΠΎΡΡ Π±ΡΡΡ ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½ΠΎ.
ΠΠ΅ΠΉΡΡΠ²ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ Π»ΡΠ±ΡΠΌ Π°ΡΠΎΠΌΠΎΠΌ.
ΠΠ°Π²Π°ΠΉΡΠ΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΠΌ ΡΠ°ΠΉΠ» lib/my_app/user.ex
, Π΄ΠΎΠ±Π°Π²ΠΈΠ² ΡΡΠ½ΠΊΡΠΈΡ validate_before_insert
:
def validate_before_insert(changeset), do: apply_action(changeset, :insert)
Π’Π΅ΠΏΠ΅ΡΡ Π΄Π°Π²Π°ΠΉΡΠ΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΠΌ ΡΠ°ΠΉΠ» lib/my_app/users/create.ex
:
@spec call(user_params()) :: {:error, Ecto.Changeset.t() | map()} | {:ok, Ecto.Schema.t()}
def call(params) do
cep = Map.get(params, :cep)
changeset = User.changeset(%User{}, params)
with {:ok, %User{}} <- User.validate_before_insert(changeset),
{:ok, %{"localidade" => city, "uf" => uf}} <- Client.get_cep_info(cep),
params <- Map.merge(params, %{city: city, uf: uf}),
changeset <- User.changeset(%User{}, params),
{:ok, %User{}} = user <- Repo.insert(changeset) do
user
end
end
Π’Π΅ΠΏΠ΅ΡΡ ΠΏΠ΅ΡΠ΅Π΄ Π²ΡΠ·ΠΎΠ²ΠΎΠΌ ViaCep ΠΌΡ ΠΏΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ Π²ΡΠ΅ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ changeset
:
ΠΠ°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ User-Agent
ΠΠ΅ΠΊΠΎΡΠΎΡΡΠ΅ Π²Π½Π΅ΡΠ½ΠΈΠ΅ API ΠΌΠΎΠ³ΡΡ Π·Π°ΠΏΡΠΎΡΠΈΡΡ ΡΡΠΎ-ΡΠΎ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΠΎΠ΅ Π΄Π»Ρ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ Π²ΡΠ·ΠΎΠ²ΠΎΠ², ΠΌΠ½ΠΎΠ³ΠΈΠ΅ ΠΌΠΎΠ³ΡΡ Π·Π°ΠΏΡΠΎΡΠΈΡΡ ΡΠΎΠΊΠ΅Π½, ΠΊΠΎΡΠΎΡΡΠΉ Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΏΠΎΠ»ΡΡΠΈΡΡ ΠΈΠ· Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ, ΠΈΠ»ΠΈ, Π² ΡΠ»ΡΡΠ°Π΅ API github, Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ User-Agent
:
Π Tesla Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π»Π΅Π³ΠΊΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ plug Tesla.Middleware.Headers
:
ΠΠ΅Ρ Httpoison:
iex> HTTPoison.get "https://api.github.com/users/maiquitome/repos", [{"User-Agent", "foobar"}]
ΠΡΠΎΡΠΈΡΠ°ΠΉΡΠ΅ Π² Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ HTTPoison ΡΠ°ΡΡΡ, ΠΏΠΎΡΠ²ΡΡΠ΅Π½Π½ΡΡ ΠΎΠΏΡΠΈΡΠΌ.
ΠΠ°ΡΠ΅ΠΌ ΠΈΠ·ΡΡΠΈΡΠ΅ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ API, ΠΊ ΠΊΠΎΡΠΎΡΠΎΠΌΡ Π²Ρ ΠΎΠ±ΡΠ°ΡΠ°Π΅ΡΠ΅ΡΡ.
ΠΠ°ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
Π ΠΊΠ°ΠΊΠΎΠΉ-ΡΠΎ ΠΌΠΎΠΌΠ΅Π½Ρ Π²Π°ΠΌ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡΡΡ ΠΏΠΎΡΡΠ΅Π±Π»ΡΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· Π²Π½Π΅ΡΠ½Π΅Π³ΠΎ API; Π² Π½Π°ΡΠ΅ Π²ΡΠ΅ΠΌΡ ΡΡΠΎ Π²ΠΏΠΎΠ»Π½Π΅ Π½ΠΎΡΠΌΠ°Π»ΡΠ½ΠΎΠ΅ ΡΠ²Π»Π΅Π½ΠΈΠ΅. Π§ΡΠ΅Π½ΠΈΠ΅ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ ΠΏΠΎ Π²Π½Π΅ΡΠ½Π΅ΠΌΡ API — ΠΏΠ΅ΡΠ²ΡΠΉ ΡΠ°Π³ ΠΊ ΡΡΠΏΠ΅Ρ Ρ. Π Elixir Ρ Π½Π°Ρ Π΅ΡΡΡ ΠΎΡΠ»ΠΈΡΠ½ΡΠ΅ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ HTTP-ΠΊΠ»ΠΈΠ΅Π½ΡΠ°, Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ ΠΏΠΎ ΠΊΠΎΡΠΎΡΡΠΌ ΡΠ°ΠΊΠΆΠ΅ ΡΡΠΎΠΈΡ ΠΏΡΠΎΡΠΈΡΠ°ΡΡ. ΠΡ Π΄ΠΎΠ»ΠΆΠ½Ρ Π±ΡΡΡ ΠΎΡΡΠΎΡΠΎΠΆΠ½Ρ ΠΈ Π½Π΅ Π΄Π΅Π»Π°ΡΡ Π»ΠΈΡΠ½ΠΈΡ Π²ΡΠ·ΠΎΠ²ΠΎΠ² Π²Π½Π΅ΡΠ½ΠΈΡ API, ΡΡΠΎΠ±Ρ Π½Π΅ ΡΠ½ΠΈΠ·ΠΈΡΡ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ Π½Π°ΡΠ΅Π³ΠΎ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ. Π‘Π»Π΅Π΄ΡΡΡΠΈΠΉ ΡΠ°Π³ — ΡΠ·Π½Π°ΡΡ, ΠΊΠ°ΠΊ ΠΏΡΠΎΠ²ΠΎΠ΄ΠΈΡΡ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ ΡΠ΅ΡΡΡ ΡΡΠΈΡ Π²Π½Π΅ΡΠ½ΠΈΡ Π²ΡΠ·ΠΎΠ²ΠΎΠ²; ΡΡΠΎ ΡΠ΅ΠΌΠ° Π΄Π»Ρ Π±ΡΠ΄ΡΡΠ΅Π³ΠΎ ΠΏΠΎΡΡΠ°.