The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Mojolicious::Plugin::InputValidation - Validate incoming requests

SYNOPSIS

  use Mojolicious::Lite;
  plugin 'InputValidation';

  # This needs to be done where one wants to use the iv_* routines.
  use Mojolicious::Plugin::InputValidation;

  post '/books' => sub {
      my $c = shift;

      # Validate incoming requests against our data model.
      if (my $error = $c->validate_json_request({
          title    => iv_any,
          abstract => iv_any(optional => 1, empty => 1),
          author   => {
              firstname => iv_word,
              lastname  => iv_word,
          },
          published => iv_datetime,
          price     => iv_float,
          revision  => iv_int,
          isbn      => iv_any(pattern => qr/^[0-9\-]{10,13}$/),
          advertise => iv_bool,
      })) {
          return $c->render(status => 400, text => $error);
      }

      # Now the payload is safe to use.
      my $payload = $c->req->json;
      ...
  };

DESCRIPTION

Mojolicious::Plugin::InputValidation compares structures against a pattern. The pattern is usually a nested structure, so the compare methods search recursively for the first non-matching value. If such a value is found a speaking error message is returned, otherwise a false value.

METHODS

Mojolicious::Plugin::InputValidation adds methods to the connection object in a mojolicous controller. This way input validation becomes easy.

validate_json_request

  my $error = $c->validate_json_request($pattern);

This method try to match the json request payload ($c->req->json) against the given pattern. If the payload matches, a false value is returned. If the payload on the other hand does not match the pattern, the first non-matching value is returned along with a speaking error message. The error message could look like:

  "Unexpected keys 'id,name' found at path /author"

TYPES

The pattern consists of one or more types the input is matched against. The following types are available.

iv_any

This is the base type for all other types. By default it matches defined values only. It supports beeing optional, means that it is okay if this element is missing entirely in the payload. When this type is marked as nillable, it also accepts a null/undef value. To accept an empty string, mark it as empty. This type supports a regex pattern to match against. All options can be combined.

  {
      foo  => iv_any,
      bar  => iv_any(optional => 1, empty => 1),
      baz  => iv_any(nillable => 1),
      quux => iv_any(pattern => qr/^new|mint|used$/),
  }
iv_int

This type matches integers, literally digits with an optional leading dash.

  {
      foo => iv_int,
      bar => iv_int(optional => 1),
      baz => iv_int(nillable => 1),
  }
iv_float

This type matches floats, so digits divided by a single dot, with an optional leading dash.

  {
      foo => iv_float,
      bar => iv_float(optional => 1),
      baz => iv_float(nillable => 1),
  }
iv_bool

This type matches booleans: true and false.

  {
      foo => iv_bool,
      bar => iv_bool(optional => 1),
      baz => iv_bool(nillable => 1),
  }
iv_word

This type is meant to match identifiers. It matches word character strings (\w+). Using the iv_any type one can achieve the same with: iv_any(pattern => qr/^\w+$/) To accept an empty string, mark it as empty.

  {
      foo => iv_word,
      bar => iv_word(optional => 1, empty => 1),
      baz => iv_word(nillable => 1),
  }
iv_datetime

This type matches datetime strings in the following format:

  YYYY-mm-DDTHH:mm:ssZ
  YYYY-mm-DDTHH:mm:ss-0100
  YYYY-mm-DDTHH:mm:ss+0000
  YYYY-mm-DDTHH:mm:ss+0100
  YYYY-mm-DDTHH:mm:ss.uuu+0100

It also supports a regex pattern, but that kinda defeats the purpose of this type.

  {
      foo  => iv_datetime,
      bar  => iv_datetime(optional => 1),
      baz  => iv_datetime(nillable => 1),
      quux => iv_datetime(pattern => qr/^\d\d\d\d-\d\d-\d\d$/,
  }
iv_object

This types matches objects (hashes). It will recurse into the elements it contains. A hash as a pattern is automatically turned into a iv_object. Using a hash is the idiomatic way, unless you need to mark it as optional or nillable.

  {
      foo => { ... },
      bar => iv_object(optional => 1, { ... }),
      baz => iv_object(nillable => 1, { ... }),
  }
iv_array - will match arrays

This type will match arrays in two different ways. For one it can match a payload against a fixed shape, and second it can match against an elemnt base type. A literal array reference ([]) is turned into an iv_array of the first kind automatically. The following is valid:

  {
      foo  => [iv_int, iv_word, ...],
      bar  => iv_array(optional => 1, [iv_int, iv_word, ...]),
      baz  => iv_array(nillable => 1, [iv_int, iv_word, ...]),
      quux => iv_array(of => iv_int, min => 1, max => 7),
  }

ALERT

This plugin is in alpha state, means it might not work at all or not as advertised.

SEE ALSO

Mojolicious, Mojolicious::Guides, https://mojolicious.org.

LICENSE AND COPYRIGHT

Copyright 2018 Tobias Leich.

This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:

http://www.perlfoundation.org/artistic_license_2_0

Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.

If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.

This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.

This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.

Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.