Parameters
params
Block
params
BlockYou should do ALL things you want with input parameters by params
block and never do it within do_block.
Make sure to declare params within params
and all undeclared params will be dropped.
There's a simple example:
params do
requires :age, type: Integer, values: 18..65
requires :sex, type: Atom, values: [:male, :female], default: :female
requires :name, type: Map do
requires :first_name
requires :last_name
end
optional :intro, type: String, regexp: ~r/^[a-z]+$/
optional :avatar, type: File
optional :avatar_url, type: String
exactly_one_of [:avatar, :avatar_url]
end
params
Variable within do_block
params
Variable within do_blockparams
variable will be given when params
dsl defined.
The initiative value of params
is conn.private[:maru_params]
Supported Parameter Types
There are a number of build-in Types:
- String
- Integer
- Float
- Boolean
- CharList
- Atom
- File
- Json
- Base64
You can also use them as :string
:integer
:float
:boolean
:char_list
:atom
:file
:json
and :base64
.
An Maru.Exceptions.InvalidFormat[reason: :illegal]
exception will be raised on type change error.
Atom Parser
Atom
type parameter is parsed byString.to_existing_atom
, so avalues
validator is recommended.
Multipart file uploads
If you are using the
File
type parameter, make sure you include the:multipart
parser in your routesRead more at https://hexdocs.pm/plug/Plug.Parsers.html#module-examples
plug Plug.Parsers, parsers: [:urlencoded, :multipart, :json], json_decoder: Poison
Custom Parameter Type
You can custom type like this:
defmodule MyColorStruct do
defstruct r: 0, g: 0, b: 0
def from_hex(<<r::binary-size(2), g::binary-size(2), b::binary-size(2)>>) do
%__MODULE__{
r: String.to_integer(r, 16),
g: String.to_integer(g, 16),
b: String.to_integer(b, 16),
}
end
end
defmodule Maru.Types.Color do
use Maru.Type
def parse("blue", _), do: "2196F3" |> MyColorStruct.from_hex
def parse("red", _), do: "F44336" |> MyColorStruct.from_hex
def parse(<<"#", s::binary-size(6)>>, _), do: s |> MyColorStruct.from_hex
end
pramams do
optional :color, type: Color
end
And you can define custom type with arguments:
defmodule Maru.Types.Time do
use Maru.Type
def arguments do
[:format]
end
def parse(input, %{format: "m/d/Y"}) do
...
end
def parse(input, %{format: "Y-m-d"}) do
...
end
end
requires :time, type: Time, format: "m/d/Y"
requires :time, type: Time, format: "Y-m-d"
Pipe Types
Pipe Types is useful for multiple encoded parameters. For example when your parameter value is a json encoded string, you can define your params
block like this:
params
group :payload, type: Json |> List do
group :alert, type: Map do
requires :id, type: Integer
requires :name, type: String
end
end
end
post do
xxx
end
curl -d 'payload=[{"alert":{"id":4399031,"name":"foo"}}]'
You can also use functions as pipe, for example:
defmodule Test do
def f(s) do
s |> String.split
end
end
requires :foo, type: fn s -> s end |> (&Test.f/1) |> List
Rename params
You can rename params like this:
params do
requires :a, type: String, source: "b"
end
And then, you will get %{a: 1}
within endpoint by params
when client send b = 1
.
Validators
There're three build-in validators: regexp
values
and allow_blank
, you can use them like this:
params do
requires :id, type: :integer, regexp: ~r/^[0-9]+$/
requires :sex, type: :atom, values: [:female, :male], default: :female
optional :age, type: :integer, values: 18..65
end
An Maru.Exceptions.UndefinedValidator
exception will be raised if validator not defined.
An Maru.Exception.Validation
exception will be raised on validators check error.
mutually_exclusive
, exactly_one_of
and at_least_one_of
can also be used in the same way as grape except that we should use an explicit List.
params do
requires :food do
optional :meat
optional :fish
optional :rice
at_least_one_of [:meat, :fish, :rice]
end
group :drink do
optional :beer
optional :wine
optional :juice
exactly_one_of [:beer, :wine, :juice]
end
optional :dessert do
optional :cake
optional :icecream
mutually_exclusive :above_all
end
end
Custom validators
defmodule Maru.Validations.Length do
def validate_param!(attr_name, value, option) do
byte(value) in option ||
Maru.Exceptions.Validation |> raise [param: attr_name, validator: :length, value: value, option: option]
end
end
params do
requires :text, length: 2..6
end
Nested Params
In general, Nested Params can be used in the same way as grape except that we should use List
and Map
in Elixir instead of Array
and Hash
in Ruby.
params do
optional :preferences, type: List do
requires :key
requires :value
end
requires :name, type: Map do
requires :first_name
requires :last_name
end
end
One Line List Params
If the type of list value is not a map, you can use List[]
to parse it. For this params parser
params do
requires :foo, type: List[Integer]
requires :bar, type: &String.split(&1, ",") |> List[Integer]
end
and this request body,
{
"foo": ["1", "2", "3"],
"bar": "1,2,3"
}
Both params[:foo]
and params[:bar]
got [1, 2, 3]
.
Reusable Params
You can define reusable params
using helpers.
defmodule API do
helpers do
params :name do
optional :first_name
optional :last_name
end
end
params do
use :name
end
get do
params[:first_name]
params[:last_name]
...
end
end
You can also define reusable params
using shared helpers.
defmodule SharedParams do
use Maru.Helper
params :period do
optional :start_date
optional :end_date
end
params :pagination do
optional :page, type: Integer
optional :per_page, type: Integer
end
end
defmodule API do
helpers SharedParams
params do
use [:period, :pagination]
end
get do
...
end
end
Updated less than a minute ago