Packages
tesla
1.4.1
1.20.0
1.18.3
1.18.2
1.18.1
1.18.0
1.17.0
1.16.0
1.15.3
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.1
1.14.0
1.13.2
1.13.1
1.13.0
1.12.3
1.12.2
1.12.1
1.12.0
1.11.2
1.11.1
1.11.0
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.1
retired
1.8.0
1.7.0
1.6.1
1.6.0
1.5.1
1.5.0
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.0
1.0.0
1.0.0-beta.1
0.10.0
0.9.0
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.2
0.5.1
0.5.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.2.2
0.2.1
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
HTTP client library, with support for middleware and multiple adapters.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/tesla/middleware/json.ex
defmodule Tesla.Middleware.JSON do
@moduledoc """
Encode requests and decode responses as JSON.
This middleware requires [Jason](https://hex.pm/packages/jason) (or other engine) as dependency.
Remember to add `{:jason, ">= 1.0"}` to dependencies.
Also, you need to recompile Tesla after adding `:jason` dependency:
```
mix deps.clean tesla
mix deps.compile tesla
```
## Examples
```
defmodule MyClient do
use Tesla
plug Tesla.Middleware.JSON # use jason engine
# or
plug Tesla.Middleware.JSON, engine: JSX, engine_opts: [strict: [:comments]]
# or
plug Tesla.Middleware.JSON, engine: Poison, engine_opts: [keys: :atoms]
# or
plug Tesla.Middleware.JSON, decode: &JSX.decode/1, encode: &JSX.encode/1
end
```
## Options
- `:decode` - decoding function
- `:encode` - encoding function
- `:encode_content_type` - content-type to be used in request header
- `:engine` - encode/decode engine, e.g `Jason`, `Poison` or `JSX` (defaults to Jason)
- `:engine_opts` - optional engine options
- `:decode_content_types` - list of additional decodable content-types
"""
@behaviour Tesla.Middleware
# NOTE: text/javascript added to support Facebook Graph API.
# see https://github.com/teamon/tesla/pull/13
@default_content_types ["application/json", "text/javascript"]
@default_encode_content_type "application/json"
@default_engine Jason
@impl Tesla.Middleware
def call(env, next, opts) do
opts = opts || []
with {:ok, env} <- encode(env, opts),
{:ok, env} <- Tesla.run(env, next) do
decode(env, opts)
end
end
@doc """
Encode request body as JSON.
It is used by `Tesla.Middleware.EncodeJson`.
"""
def encode(env, opts) do
with true <- encodable?(env),
{:ok, body} <- encode_body(env.body, opts) do
{:ok,
env
|> Tesla.put_body(body)
|> Tesla.put_headers([{"content-type", encode_content_type(opts)}])}
else
false -> {:ok, env}
error -> error
end
end
defp encode_body(%Stream{} = body, opts), do: {:ok, encode_stream(body, opts)}
defp encode_body(body, opts) when is_function(body), do: {:ok, encode_stream(body, opts)}
defp encode_body(body, opts), do: process(body, :encode, opts)
defp encode_content_type(opts),
do: Keyword.get(opts, :encode_content_type, @default_encode_content_type)
defp encode_stream(body, opts) do
Stream.map(body, fn item ->
{:ok, body} = encode_body(item, opts)
body <> "\n"
end)
end
defp encodable?(%{body: nil}), do: false
defp encodable?(%{body: body}) when is_binary(body), do: false
defp encodable?(%{body: %Tesla.Multipart{}}), do: false
defp encodable?(_), do: true
@doc """
Decode response body as JSON.
It is used by `Tesla.Middleware.DecodeJson`.
"""
def decode(env, opts) do
with true <- decodable?(env, opts),
{:ok, body} <- decode_body(env.body, opts) do
{:ok, %{env | body: body}}
else
false -> {:ok, env}
error -> error
end
end
defp decode_body(body, opts), do: process(body, :decode, opts)
defp decodable?(env, opts), do: decodable_body?(env) && decodable_content_type?(env, opts)
defp decodable_body?(env) do
(is_binary(env.body) && env.body != "") || (is_list(env.body) && env.body != [])
end
defp decodable_content_type?(env, opts) do
case Tesla.get_header(env, "content-type") do
nil -> false
content_type -> Enum.any?(content_types(opts), &String.starts_with?(content_type, &1))
end
end
defp content_types(opts),
do: @default_content_types ++ Keyword.get(opts, :decode_content_types, [])
defp process(data, op, opts) do
case do_process(data, op, opts) do
{:ok, data} -> {:ok, data}
{:error, reason} -> {:error, {__MODULE__, op, reason}}
{:error, reason, _pos} -> {:error, {__MODULE__, op, reason}}
end
rescue
ex in Protocol.UndefinedError ->
{:error, {__MODULE__, op, ex}}
end
defp do_process(data, op, opts) do
# :encode/:decode
if fun = opts[op] do
fun.(data)
else
engine = Keyword.get(opts, :engine, @default_engine)
opts = Keyword.get(opts, :engine_opts, [])
apply(engine, op, [data, opts])
end
end
end
defmodule Tesla.Middleware.DecodeJson do
@moduledoc false
def call(env, next, opts) do
opts = opts || []
with {:ok, env} <- Tesla.run(env, next) do
Tesla.Middleware.JSON.decode(env, opts)
end
end
end
defmodule Tesla.Middleware.EncodeJson do
@moduledoc false
def call(env, next, opts) do
opts = opts || []
with {:ok, env} <- Tesla.Middleware.JSON.encode(env, opts) do
Tesla.run(env, next)
end
end
end