Mojolicious-Plugin-InputValidation

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

  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}$/),

lib/Mojolicious/Plugin/InputValidation.pm  view on Meta::CPAN

    }

    if (defined $self->{min} && $elems < $self->{min}) {
        $self->error(sprintf("Too few elements in array (%d vs %d) at path %s",
            $elems, $self->{min}, $path || '/'));
        return 0;
    }

    if ($self->{of}) {
        for (my $i = 0; $i < ($self->{max} // $elems); $i++) {
            my $err = Mojolicious::Plugin::InputValidation::_validate_structure($value->[$i], $self->{of}, "$path/$i");

            if ($err) {
                $self->error($err);
                return 0;
            }
        }
    }
    elsif ($self->{pattern} && !$self->{min} && !$self->{min}) {
        for (my $i = 0; $i < scalar @{$self->{pattern}}; $i++) {
            my $err = Mojolicious::Plugin::InputValidation::_validate_structure($value->[$i], $self->{pattern}[$i], "$path/$i");

            if ($err) {
                $self->error($err);
                return 0;
            }
        }
    }
    else {
        $self->error('Error: illegal pattern for array at path ' . ($path // '/'));
        return 0;

lib/Mojolicious/Plugin/InputValidation.pm  view on Meta::CPAN

        $self->error(sprintf("Unexpected keys '%s' found at path %s", join(',', @unexpected), $path || '/'));
        return 0;
    }

    if (@missing) {
        $self->error(sprintf("Missing keys '%s' at path %s", join(',', @missing), $path || '/'));
        return 0;
    }

    for my $key (grep { $have_keys{$_} } @want_keys) {
        my $err = Mojolicious::Plugin::InputValidation::_validate_structure($value->{$key}, $self->{pattern}{$key}, "$path/$key");

        if ($err) {
            $self->error($err);
            return 0;
        }
    }

    return 1;
}

lib/Mojolicious/Plugin/InputValidation.pm  view on Meta::CPAN

    monkey_patch $caller, 'iv_int',      \&iv_int;
    monkey_patch $caller, 'iv_float',    \&iv_float;
    monkey_patch $caller, 'iv_bool',     \&iv_bool;
    monkey_patch $caller, 'iv_word',     \&iv_word;
    monkey_patch $caller, 'iv_any',      \&iv_any;
}

sub register {
    my ($self, $app, $conf) = @_;

    $app->helper(validate_json_request => sub {
        my ($c, $pattern) = @_;
        return _validate_structure($c->req->json, $pattern);
    });
    $app->helper(validate_params => sub {
        my ($c, $pattern) = @_;
        return _validate_structure($c->params, $pattern);
    });
    $app->helper(validate_structure => sub {
        my ($c, $structure, $pattern) = @_;
        return _validate_structure($structure, $pattern);
    });
}

sub _validate_structure {
    my ($input, $pattern, $path) = @_;

    if (ref $pattern eq 'HASH') {
        $pattern = iv_object($pattern);
    }
    elsif (ref $pattern eq 'ARRAY') {
        $pattern = iv_array($pattern);
    }

    return sprintf("Error: pattern '%s' must be of kind iv_*", $pattern)

lib/Mojolicious/Plugin/InputValidation.pm  view on Meta::CPAN

  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}$/),

lib/Mojolicious/Plugin/InputValidation.pm  view on Meta::CPAN

L<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.

=head1 METHODS

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

=head2 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"

=head1 TYPES

t/01-input-validation.t  view on Meta::CPAN


is err({ foo => [{ bar => 'flubber-worms47' }] }, { foo => iv_array(of => { bar => iv_any(pattern => qr/(flubber-worms47|test|test2)/) }) }),
    '', 'hash with array of hashes (1)';

is err({ foo => [{ bar => 'worms47' }] }, { foo => iv_array(of => { bar => iv_any(pattern => qr/(flubber-worm47|test|test2)/) }) }),
    "Value 'worms47' does not match at path /foo/0/bar",
    'hash with array of hashes (2)';

done_testing;

sub err { Mojolicious::Plugin::InputValidation::_validate_structure(@_) }

t/02-mojo-lite.t  view on Meta::CPAN

use Test::More;
use Test::Mojo;
use lib 'lib';
use Mojolicious::Lite;

plugin 'InputValidation';
use Mojolicious::Plugin::InputValidation;

post '/' => sub {
    my $c = shift;
    $c->render(text => $c->validate_json_request({
        foo => {
            bar  => iv_int,
            baz  => iv_word,
            quux => iv_float(optional => 1),
        }
    }));
};

my $web = Test::Mojo->new;

t/03-boolean.t  view on Meta::CPAN

use Test::More;
use Test::Mojo;
use lib 'lib';
use Mojolicious::Lite;

plugin 'InputValidation';
use Mojolicious::Plugin::InputValidation;

post '/' => sub {
    my $c = shift;
    $c->render(text => $c->validate_json_request({
        foo => iv_bool,
        bar => iv_bool(empty => 1),
        baz => iv_bool(nillable => 1),
    }));
};

my $web = Test::Mojo->new;

$web->post_ok('/' => { 'Content-Type' => 'application/json' } => '{ "foo": true, "bar": false, "baz": null }')
    ->content_is('');



( run in 1.577 second using v1.01-cache-2.11-cpan-39bf76dae61 )