From d4fb6571b38226bd9903ff9db0bce28abcad80d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sun, 23 Jul 2017 15:30:32 +0200 Subject: Update the constraints chapter --- doc/src/guide/constraints.asciidoc | 74 +++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 21 deletions(-) (limited to 'doc') diff --git a/doc/src/guide/constraints.asciidoc b/doc/src/guide/constraints.asciidoc index 7721831..6cc1075 100644 --- a/doc/src/guide/constraints.asciidoc +++ b/doc/src/guide/constraints.asciidoc @@ -5,7 +5,7 @@ Constraints are validation and conversion functions applied to user input. They are used in various places in Cowboy, including the -router and the request match functions. +router and the `cowboy_req` match functions. === Syntax @@ -36,10 +36,18 @@ check that the integer is positive: [source,erlang] ---- -PositiveFun = fun(V) when V > 0 -> true; (_) -> false end, +PositiveFun = fun + (_, V) when V > 0 -> + {ok, V}; + (_, _) -> + {error, not_positive} +end, {my_value, [int, PositiveFun]}. ---- +We ignore the first fun argument in this snippet. We shouldn't. +We will simply learn what it is later in this chapter. + When there's only one constraint, it can be provided directly without wrapping it into a list: @@ -62,30 +70,54 @@ Built-in constraints are specified as an atom: === Custom constraints Custom constraints are specified as a fun. This fun takes -a single argument and must return one of `true`, `{true, NewValue}` -or `false`. +two arguments. The first argument indicates the operation +to be performed, and the second is the value. What the +value is and what must be returned depends on the operation. -`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: +Cowboy currently defines three operations. The operation +used for validating and converting user input is the `forward` +operation. [source,erlang] ---- -fun (Value0) when is_binary(Value0) -> - try binary_to_integer(Value0) of - Value -> {true, Value} +int(forward, Value) -> + try + {ok, binary_to_integer(Value)} catch _:_ -> - false - end. + {error, not_an_integer} + end; +---- + +The value must be returned even if it is not converted +by the constraint. + +The `reverse` operation does the opposite: it +takes a converted value and changes it back to what the +user input would have been. + +[source,erlang] +---- +int(reverse, Value) -> + try + {ok, integer_to_binary(Value)} + catch _:_ -> + {error, not_an_integer} + end; +---- + +Finally, the `format_error` operation takes an error +returned by any other operation and returns a formatted +human-readable error message. + +[source,erlang] +---- +int(format_error, {not_an_integer, Value}) -> + io_lib:format("The value ~p is not an integer.", [Value]). ---- -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. +Notice that for this case you get both the error and +the value that was given to the constraint that produced +this error. -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. +Cowboy will not catch exceptions coming from constraint +functions. They should be written to not emit any exceptions. -- cgit v1.2.3