パルカワ2

最近はFlutterをやっています

form validatorについて考えた

前提

  • Ark
  • form validatorとdata validatorは役割が違うもの。
  • 同じものを完全に使いまわすのは現状難しい。

結論

最終的にこうすると決めた。

package MyApp::FormValidator;
use 5.10.1;
use strict;
use warnings;
use FormValidator::Lite;

sub validate {
    my ($class, $name, $req) = @_;

    my $spec = $class->specs->{$name} or return;
    my $fvl  = FormValidator::Lite->new($req);

    $fvl->check(%$spec);
    if ($fvl->has_error) {
        my $message_data = {
            message  => {},
            param    => { map { $_ => $_ } keys %$spec },
            function => $class->function_messages,
        };
        $fvl->set_message_data($message_data);
    }

    $fvl;
}

sub specs {
    state $specs = {
        "api/signup" => {
            challenge => ["NOT_NULL", ["LENGTH", 64]],
            response  => ["NOT_NULL", ["LENGTH", 64]],
        },
    };
}

sub function_messages {
    state $function_messages = {
        not_null => "[_1] is empty",
        length   => "[_1] is too small/big",
    };
}

1;

specsまわりは変わるかもしれないけど、こんな感じのを作った。
そんで、アトリビュートを使うようにした。

package MyApp::ActionClass::ValidateRequest;
use utf8;
use Mouse::Role;

around ACTION => sub {
    my $next = shift;
    my ($self, $action, $c, @args) = @_;

    if ($action->attributes->{ValidateRequest}[0]) {
        require MyApp::FormValidator;
        my $v = MyApp::FormValidator->validate($c->req->action->reverse, $c->req);

        if ($v && $v->has_error) {
            $c->forward(
                "/api/error_with_messages",
                [$v->get_error_messages], 400,
            );
            return;
        }
    }

    $next->(@_);
};

no Mouse::Role;

sub _parse_ValidateRequest_attr {
    return ValidateRequest => 1;
}

1;

そんで、Controllerの中でこんな感じ。

package MyApp::Controller::API;
use Mouse;
use Ark "Controller";
with "MyApp::ActionClass::ValidateRequest";

sub signup :ValidateRequest :Local {
    ...;
}

もっといい方法あるかもしれない。
最初autoでやろうと思ったのだけど、わかりにくいと思ってやめた。