PEF::Front model methods description
This is the crucial feature of PEF::Font.
SYNOPSIS
/submitUserLogin
# model/UserLogin.yaml
---
params:
ip:
value: context.ip
login:
min-size: 1
max-size: 40
password:
min-size: 4
max-size: 40
model: User::login
result:
OK:
set-cookie:
auth:
value: TT response.auth
expires: TT response.expires
redirect: /me
DEFAULT:
unset-cookie: auth
/ajaxGetArticles
# model/GetArticles.yaml
---
params:
ip:
value: context.ip
limit:
regex: ^\d+$
max-size: 3
offset:
regex: ^\d+$
max-size: 10
model: Article::get_articles
DESCRIPTION
For every internal API method there's one YAML-file describing its parameters, handler method, caching and result processing.
There're two sources to call these methods from: templates and HTTP requests.
To call this method from template you write it like this:
[% articles = "get articles".model(offset => articles_offset, limit => 5) %]
To call this method from AJAX you send HTTP request like this:
GET /ajaxGetArticles?offset=0&limit=5
"Normal" method name is lowercased phrase with spaces. Model methods description file name is made up from method name transformed to CamelCase with '.yaml' extension: "get articles" -> "GetArticles.yaml". HTTP request method name is made up concatenating one of prefixes '/ajax', '/submit', '/get' and CamelCase form of method name.
INPUT PARAMETERS
Section "params" describes method input parameters, their sources, types and checks. There're two parameter's attribute to set source: value and default. value means unconditionally set value; default means that value will be set only if it was not set from request query or form data parameter.
Framework automatically decodes input data into internal Perl UTF-8 and automatically encodes output to UTF-8. This is the only supported encoding.
Possible sources
- from query string or form data
-
This is the default. There's no difference between parameters from query string and form data. But when the same parameter is in query and form data then value from query has precedence.
There's also special parameter json that has to be encoded JSON value. When it's present, then parameters are overwritten from this decoded JSON.
Request parsing detects JSON or XML content-types and can parse them.
- Direct value
-
value and default can be string or integer.
--- params: ip: default: 127.0.0.1 is_active: default: 0
- from context data
-
context is a hash with some data for handlers. It is created after initial routing processing before template or API method processing. There're some data:
- ip
-
IP address of the client.
- lang
-
Short (ISO 639-1) language code. There's automatic language detection based on URL, HTTP headers and cookies and Geo IP. You can turn it off. It's written to 'lang' cookie.
- hostname
-
Hostname of current request.
- path
-
Current URI path.
- path_info
-
Initial URI path.
- method
-
Method name.
- scheme
-
URL scheme. One of: 'http', 'https'.
- src
-
"Source" of the request. One of: 'app', 'ajax', 'submit', 'get'.
-
They are also parts of context but they can't be used as values by themselves. They are used in handlers.
session
is loaded only if it was used for parameter value. - template
-
For template processing "method" is replaced with "template" which is template name.
- time, gmtime, localtime
-
These are additional fields for template processing.
time
is current UNIX-time,gmtime
- 9-element list with the time in GMT,localtime
- 9-element list with the time for the local time zone. Seeperldoc -f
for these functions.
Example:
--- params: ip: value: context.ip lang: default: context.lang
- from request parameters
-
By default parameter "param1" is set from "param1" query/form data, but it's possible to set it from another request parameter, like "another_param". form is meant for this.
--- params: ip: value: context.ip lang: default: context.lang login: value: form.username
- from headers
-
--- params: ip: value: context.ip back_url: default: headers.referer
-
--- params: ip: value: context.ip auth: default: cookies.auth
- from request notes
-
Routing subroutines can set some notes on request object. Notes are just any key-value pairs.
--- params: ip: value: context.ip subsection: default: notes.subsection
- from session data
-
From request parameters or from cookies using cfg_session_request_field is possible to automatically load value from session data.
--- params: ip: value: context.ip user_last_name: default: session.user_last_name
- from configuration parameter
-
You can specify any configuration parameter of framework or your application.
--- params: path: value: config.avatar_images_path
Checks
Ther're several types of input data checks.
- Perl regular expressions
-
This is the mostly used method. Regexp::Common with option 'RE_ALL' is already loaded. When you write an expressions as parameter value then it means regexp check.
--- params: positive_integer: ^\d+$ integer_or_empty: ^\d+$|^$ any_integer: ^$RE{num}{int}$ money: ^$RE{num}{decimal}{-places=>"0,2"}$
When you need to add some other attribute then attribute 'regex' is used:
--- params: lang: default: context.lang regex: ^[a-z]{2}$
- Possible values
-
Atributes
can
,can_string
andcan_number
describes set of possible values.can
andcan_string
are synonyms.--- params: bool: can_number: [0, 1] default: 0 lang: can: [en, de] default: de
- Type
-
Parameter can have type 'array', 'hash' or 'file'. You can specify this with last symbol of the parameter name '@', '%' or '*' or with attribute 'type' as 'array', 'hash' or 'file'. To submit array you can use PHP-syntax:
<!-- HTML --> <select name="select[]" multiple="multiple"> # YAML --- params: select@:
Another way to submit array or hash is to use
json
form data field or to post JSON or XML content.Array of files has type 'array'.
- Maximum or minimum size
-
This checks are working according to parameter type:
Min/max values are included in allowed range.
--- params: limit40str: max-size: 40 limit4_40str: min-size: 4 max-size: 40
- Maximum or minimum value
-
Numerical checks. Min/max values are included in allowed range.
--- params: speed: max: 140 min: 20
- captcha
-
Captcha validation process usually removes information from captcha database and this check can be done only once. Validation process makes all needed checks.
This works following way. One parameter contains entered captcha code and another parameter contains hashed data of the right captcha code. Attribute
captcha
specifies parameter with hashed data of the right captcha code. Validator checks whether the code is right. If entered captcha is equal to 'nocheck' then it means to validator 'no need to check captcha' and no check is done. If captcha is required then handler must check that entered captcha code is not equal to 'nocheck'. - Optional flag
-
Attribute
optional
specifies whether parameter is optional. Special value 'empty' means parameter is optional even when passed but contains only empty string. - Custom filter
-
Filter can be a Perl subroutine, regular expression substitution or array of regular expression substitutions.
--- params: auth: default: cookies.auth max-size: 40 filter: Auth::required comment: max-size: 1000 filter: [ s/</</g, s/>/>/g ]
Recognized substitution operators are:
s
,tr
andy
.Auth::required
actually means subroutinerequired($value, $context)
from your module${YourApplicationNamespace}::InFilter::Auth
.I.e.:
+ $project_dir/ + $app/ + $Project/ - AppFrontConfig.pm + InFilter/ - Auth.pm
Your subroutine recieves 2 arguments: value of the parameter and request context.
package MyApp::InFilter::Auth; use DBIx::Struct; use MyApp::Common; sub required { my ($value, $context) = @_; my $author = get_author_from_auth($value); die { result => "NEED_LOGIN", answer => 'You have to login for this operation' } unless $author; $value; }
When filter dies for optional parameter then this parameter is deleted from request as if it was not provided. For required fields this means failed validation.
- Kind of inheritance
-
There's special YAML-file -base-.yaml in your cfg_model_dir where you can write all common requests parameters and just use these descriptions in your model methods description files.
-base-.yaml --- params: ip: value: context.ip auth: default: cookies.auth max-size: 40 auth_required: base: auth filter: Auth::required limit: regex: ^\d+$ max-size: 3 offset: regex: ^\d+$ max-size: 10 positive_integer: ^\d+$ integer_or_empty: ^\d+$|^$ any_integer: ^$RE{num}{int}$ bool: can_number: [0, 1] default: 0 money: ^$RE{num}{decimal}{-places=>"0,2"}$
Attribute
base
allows to specify "inheritance" of the properties for corresponding parameter from -base-.yaml. It works even inside -base-.yaml. When you write an expressions with leading$
as parameter value then it means "inheritance".--- params: ip: $ip auth: base: $auth optional: true id_article: $positive_integer id_comment_parent: base: $integer_or_empty filter: Empty::to_undef author: base: $limit40str min-size: 1 filter: Default::auth_to_author
Extra parameters
Special key extra_params
controls what to do when there're more parameters as required: ignore - all extra parameters will be silently deleted; pass - all extra parameters will be passed without validation; disallow - validation fails when extra parameters passed.
---
params:
ip: $ip
extra_params: pass
Output result
Handlers are supposed to return hash like these.
{
result => "OK",
}
or
{
result => "SOMEERRCODE",
answer => 'Some $1 Error $2 Message $3',
answer_args => [$some, $error, $params],
}
result
is required in every answer, other keys are optional.
Following keys have some meaning:
- result
-
Is symbolic result state.
- "OK" means everything is good
- "INTERR" means some internal application error
- "OAUTHERR" means Oauth protocol error
- "BADPARAM" means validation error
Application can use its own codes.
- answer
-
Message from application. If calling type is
/ajax
andanswer_no_nls
is not present or false then this message will be automatically localized.When calling type is
/get
or/submit
then value of this key can be open file handle or code reference. If value of this key is code reference then it is "streaming function". See "Delayed Response and Streaming Body" in PSGI. - answer_args
-
Array of arguments to the message.
- answer_headers
-
Array of key-value pairs that will be set as output HTTP headers and their values. Key-value pairs can be list ($header => $value), array [$header => $value] or hash {$header => $value}.
[ {'X-Hr' => 'x-hr'}, ['X-Ar' => 'x-ar'], 'X-Header' => 'x-value' ]
-
Array of key-value pairs that will be set as output HTTP cookies and their values. Key-value pairs can be list ($cookie => $value), array [$cookie => $value] or hash {$cookie => $value}.
[ {ch => 'Chv'}, [ca => 'Cav'], Cookie => 'cookie_value' ]
- answer_no_nls
-
If present and true then
answer
will be send as is, without localization attempt. - answer_status
-
Sets HTTP status code. If
answer_status
is between 300 and 400 thenLocation
headers specifys new location for redirect. - answer_content_type
-
Sets Content-Type header.
- answer_data
-
Replaces answer hash with
answer_data
field before encoding to JSON. It can be only ARRAY or HASH reference. - answer_http_response
-
Uses supplied HTTP response object as handler response.
When method is called like /ajax
then it means JSON format answer. When you need another output format, use /get
or /submit
type calls.
Response caching
Some methods can return constant or rarely changing data, it makes perfect sense to cache them. Key cache
manages caching for responses. It has to attributes: key
- one value or array defining caching key; expires
- how long data can be retained in cache. This value is parsed by Time::Duration::Parse.
---
params:
id_user: $positive_integer
cache:
key: [method, id_user]
expires: 1m
Result processing
Response key result
defines result
's section to execute some actions. When no section is found then it looks for DEFAULT
section. Following actions are supported:
- redirect
-
Temporary browser redirect (302) for
/get
or/submit
request types. Can be array or values - it finds first non-empty. -
Possible attributes:
value
,expires
,domain
,path
,secure
,max-age
,httponly
.secure
attribute can be calculated automatically from request's scheme when setting or unsetting cookie if not explicitly set in attributes. -
Unsets cookie. It can be one cookie name, list of cookies or hash of cookies with their attributes as for
set-cookie
.Cookie with attributes can be required when cookie was set with some attributes like
domain
,path
,secure
. In this case you have to nail the right cookie.Usually you don't need to set any attributes and then unset will work automatically.
- filter
-
Specifies output filter.
- answer
-
Sets answer content.
- set-header
-
Sets response header. This action ensures that there's only one header with given name in response.
- add-header
-
Adds response header. This action allows to have multiple headers with the same name.
---
params:
back_url:
default: headers.referer
result:
OK:
set-cookie:
auth:
value: TT response.auth
expires: TT response.expires
redirect: /me
DEFAULT:
unset-cookie: auth
redirect: TT request.back_url
Prefix TT
means "process this expression with Template-Toolkit language". This prefix can be used for any attribute value in result
section.
Stash for this processing contains following data fields: response
, form
, cookies
, context
, request
, result
.
There're two additional helper functions: uri_unescape($uri)
and session($key)
. $key is optional.
Output filter
It's possible to specify filtering function for sending content. Attribute filter
for given result code
in result
section specifies function that accepts two parameters: ($response, $context).
---
result:
OK:
filter: Export::toXML
Export::toXML
actually means subroutine toXML($response, $context)
from your module ${YourApplicationNamespace}::OutFilter::Export
.
$response must be modified "in-place", return value of subroutine is ignored.
Intended application is to transform response to external form, like XML, CSV, XLS and so on.
Model handler
Key model
sets calling model handler. Model can be "local" or "remote". "Local" means that handler is right inside loaded application. "Remote" can mean everything, like "call database method". "Remote" model is handled by cfg_model_rpc($model_handler)
. "Local" is located in some module inside ${YourApplicationNamespace}::Local::$Module
.
When you need to call some handler outside of "Local::" namespace, prepend its name with '^', like ^Some::Lib::Module::handler
.
---
params:
ip: $ip
login:
base: limit40str
min-size: 1
password: $limit4_40str
model: Author::login
# $project_dir/$app/$Project/Local/Author.pm
package MyApp::Local::Author;
sub login {
my ($req, $context) = @_;
my $author = one_row(author => {hash_ref_slice $req, qw(login password)})
or return {result => "PASS"};
my $auth = PEF::Front::Session::_secure_value;
new_row( 'author_auth',
id_author => $author->id_author,
auth => $auth,
expires => [\"now() + ?::interval", $expires]
);
return {
result => "OK",
expires => $expires,
auth => $auth
};
}
Model handler that matches /^\^?\w+::/ is "local" otherwise it is "remote".
By default, cfg_model_rpc($model_handler)
calls PEF::Front::Model::chain_links
which accepts list of handler functions to execute with optional parameters. These functions receive ($req, $context, $previous_response[, $optional_params])
. Return value from last function is the return value from model handler.
Method call types
Model method can be called from AJAX or template. URI path prefix (src
in context) defines how method was called and what type of result is expected.
- /ajax
-
Answer will be in JSON format and redirects from
result
section are ignored. - /submit
-
Answer can be in any format and usually redirects to new location.
- /get
-
Works like /submit but by default parses rest of the path to parameters. See cfg_parse_extra_params($src, $params, $form) in PEF::Front::Config.
Method that is called from templates has src
equal to 'app'.
Key allowed_source
can restrict call types: template
- for templates; submit
- for /submit
or /get
; ajax
for /ajax
. Really useful distinction is when some method are allowed to be called only from templates.
When this key is absent then there's no restrictions.
Captcha
It's supposed to be used like this. You make method making captcha images:
Captcha.yaml:
---
params:
width:
default: 35
height:
default: 40
size:
default: 5
extra_params: ignore
model: PEF::Front::Captcha::make_captcha
You use it in some template like this:
<form method="post" action="/submitSendMessage">
Captcha:
[% captcha="captcha".model %]
<input type="hidden" name="captcha_hash" value="[% captcha.code %]">
<img src="[% config('www_static_captchas_path') %][% captcha.code %].jpg">
<input type="text" maxlength="5" name="captcha_code">
...
</form>
SendMessage.yaml:
---
params:
ip: $ip
email: $email
lang: $lang
captcha_code:
min-size: 5
captcha: captcha_hash
captcha_hash:
min-size: 5
subject: $subject
message: $message
result:
OK:
redirect: /appSentIsOk
model: Article::add_comment
allowed_source: [ajax, submit]
Your MyApp::Local::Article::add_comment subroutine must check that captcha_code
is not equal to 'nocheck' if captcha is required. For example, if user is already logged in then it makes little sense to enter captcha.
Capthcha image is made by PEF::Front::Captcha::make_captcha($request, $context)
function. This function writes generated images in cfg_www_static_captchas_dir, stores some information in its own database in cfg_captcha_db and returns generated md5 sum. When form is submitted, validator checks the entered code and when it is right, passes to the model method "send message". Captcha code checks are destructive: the code is not valid anymore after successful check.
To change generation image class see cfg_captcha_image_class in PEF::Front::Config.
File upload
Uploaded files are stored in some subdirectory in cfg_upload_dir. Corresponding parameter value is object of PEF::Front::File. This parameter can have attribute type 'file' to pass validation. Array of files can have attribute 'array'.
Uploaded file should be copied to some permanent storage. Usually you can just link
it to new place. Uploaded files will be deleted after request destruction.
AUTHOR
This module was written and is maintained by Anton Petrusevich.
Copyright and License
Copyright (c) 2016 Anton Petrusevich. Some Rights Reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.