mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Rework the constraints chapter
This commit is contained in:
parent
2a6359ecc1
commit
8777f631ca
1 changed files with 69 additions and 32 deletions
|
@ -1,54 +1,91 @@
|
|||
[[constraints]]
|
||||
== Constraints
|
||||
|
||||
Cowboy provides an optional constraints based validation feature
|
||||
when interacting with user input.
|
||||
Constraints are validation and conversion functions applied
|
||||
to user input.
|
||||
|
||||
Constraints are first used during routing. The router uses
|
||||
constraints to more accurately match bound values, allowing
|
||||
to create routes where a segment is an integer for example,
|
||||
and rejecting the others.
|
||||
They are used in various places in Cowboy, including the
|
||||
router and the request match functions.
|
||||
|
||||
Constraints are also used when performing a match operation
|
||||
on input data, like the query string or cookies. There, a
|
||||
default value can also be provided for optional values.
|
||||
=== Syntax
|
||||
|
||||
Finally, constraints can be used to not only validate input,
|
||||
but also convert said input into proper Erlang terms, all in
|
||||
one step.
|
||||
Constraints are provided as a list of fields. For each field
|
||||
in the list, specific constraints can be applied, as well as
|
||||
a default value if the field is missing.
|
||||
|
||||
=== Structure
|
||||
A field can take the form of an atom `field`, a tuple with
|
||||
constraints `{field, Constraints}` or a tuple with constraints
|
||||
and a default value `{field, Constraints, Default}`.
|
||||
The `field` form indicates the field is mandatory.
|
||||
|
||||
Constraints are provided as a list of fields and for each
|
||||
field a list of constraints for that field can be provided.
|
||||
Note that when used with the router, only the second form
|
||||
makes sense, as it does not use the default and the field
|
||||
is always defined.
|
||||
|
||||
Fields are either the name of the field; the name and
|
||||
one or more constraints; or the name, one or more constraints
|
||||
and a default value.
|
||||
Constraints for each field are provided as an ordered list
|
||||
of atoms or funs to apply. Built-in constraints are provided
|
||||
as atoms, while custom constraints are provided as funs.
|
||||
|
||||
When no default value is provided then the field is required.
|
||||
Otherwise the default value is used.
|
||||
When multiple constraints are provided, they are applied in
|
||||
the order given. If the value has been modified by a constraint
|
||||
then the next one receives the new value.
|
||||
|
||||
All constraints for a field will be used to match its value
|
||||
in the order they are given. If the value is modified by a
|
||||
constraint, the next constraint receives the updated value.
|
||||
For example, the following constraints will first validate
|
||||
and convert the field `my_value` to an integer, and then
|
||||
check that the integer is positive:
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
PositiveFun = fun(V) when V > 0 -> true; (_) -> false end,
|
||||
{my_value, [int, PositiveFun]}.
|
||||
----
|
||||
|
||||
When there's only one constraint, it can be provided directly
|
||||
without wrapping it into a list:
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{my_value, int}
|
||||
----
|
||||
|
||||
=== Built-in constraints
|
||||
|
||||
Built-in constraints are specified as an atom:
|
||||
|
||||
[cols="<,<",options="header"]
|
||||
|===
|
||||
| Constraint | Description
|
||||
| int | Convert binary value to integer.
|
||||
| int | Converts binary value to integer.
|
||||
| nonempty | Ensures the binary value is non-empty.
|
||||
|===
|
||||
|
||||
=== Custom constraint
|
||||
=== Custom constraints
|
||||
|
||||
In addition to the predefined constraints, Cowboy will accept
|
||||
a fun. This fun must accept one argument and return one of
|
||||
`true`, `{true, NewValue}` or `false`. The result indicates
|
||||
whether the value matches the constraint, and if it does it
|
||||
can optionally be modified. This allows converting the value
|
||||
to a more appropriate Erlang term.
|
||||
Custom constraints are specified as a fun. This fun takes
|
||||
a single argument and must return one of `true`, `{true, NewValue}`
|
||||
or `false`.
|
||||
|
||||
Note that constraint functions SHOULD be pure and MUST NOT crash.
|
||||
`true` indicates the input is valid, `false` otherwise.
|
||||
The `{true, NewValue}` tuple is returned when the input
|
||||
is valid and the value has been converted. For example,
|
||||
the following constraint will convert the binary input
|
||||
to an integer:
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
fun (Value0) when is_binary(Value0) ->
|
||||
try binary_to_integer(Value0) of
|
||||
Value -> {true, Value}
|
||||
catch _:_ ->
|
||||
false
|
||||
end.
|
||||
----
|
||||
|
||||
Constraint functions should only crash because the programmer
|
||||
made an error when chaining constraints incorrectly (for example
|
||||
if the constraints were `[int, int]`, and not because of input.
|
||||
If the input is invalid then `false` must be returned.
|
||||
|
||||
In our snippet, the `is_binary/1` guard will crash only
|
||||
because of a programmer error, and the try block is there
|
||||
to ensure that we do not crash when the input is invalid.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue