前提
- 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でやろうと思ったのだけど、わかりにくいと思ってやめた。