NAME
Dancer2::Plugin::WebService - Rest APIs with login, persistent data, multiple in/out formats, IP security, role based access
VERSION
version 4.8.1
SYNOPSIS
get '/my_keys' => sub { reply { 'k1'=>'v1' , 'k2'=>'v2' } };
curl $url/my_keys
DESCRIPTION
Create REST APIs with login, logout, persistent session data, IP security, role based access. Multiple input/output supported formats : json , xml , yaml, perl , human Post your data and keys as url parameters or content body text
curl -X GET "$url/SomeRoute?k1=v1&k2=v2&k3=v3"
curl -X POST $url/SomeRoute -d '{ "k1":"v1", "k2":"v2", "k3":"v3" }'
NAME
Convert your functions to REST api with minimal effort
URL parameters to format the reply
You can use the from, to, sort, pretty parameters to define the input/output format
- from , to
-
Define the input/output format.
You can define input/output formats independently. from default is the config.yml property plugins.TestService.Default format = json Supported formats are
json or jsn yaml or yml xml perl human or text or txt curl "$url/mirror?from=perl&to=xml" -d '{ "k1" => ["v1","v2","v3"] }' - sort
-
If true the keys are returned sorted. The default is false because it is faster. Valid values are true, 1, yes, false, 0, no
- pretty
-
If false, the data are returned as one line compacted. The default is true, for human readable output. Valid values are true, 1, yes, false, 0, no
METHODS
Plugin methods available for your main Dancer2 code
UserData
Get all or some of the posted data Retruns a hash referense if the data are posted as hash Retruns an array referense if the data are posted as list
UserData Returns everything
UserData('k1','k2') Returns only the specific keys of the posted hash/list
get '/SomePath' => sub { reply UserData };
reply
Your last route's statement. Accepts a Perl data structure, and return it as json, xml, yaml, perl or human under the key reply
reply( 'hello world' )
reply( \'hello world' )
reply( 'a', 'b' , 'c' )
reply( [ 'a', 'b' , 'c' ] )
reply( { k1=>'v1', k2=>'v2' } )
reply( &SomeFunction )
reply( \&SomeFunction )
Error
Set the error. Normally at success error should be 0 It does not stop the route execution. You must place it before the reply()
get '/SomePath' => sub { Error('ok'); reply 'hello world' };
get '/SomePath' => sub { Error('oups') };
get '/SomePath' => sub { reply 'a', 'b' };
SessionSet
Store session persistent data, unlike the volatile common posted data. It is a protected method, login is required
They are persistent between requests until they deleted, the user logout or their session get expired.
You must pass your data as hash or hash reference.
Returns a list of the stored keys.
any['get','post'] => '/session_save' => sub
{
@arr = SessionSet( k1=>'v1' , k2=>'v2' );
@arr = SessionSet( { k3=>'v3' , k4=>'v4' } );
reply { 'Your saved keys are' => \@arr }
};
curl $url/session_save?token=17398-5c8a71b -H "$H" -X POST -d '{"k1":"v1", "k2":"v2", ... }'
SessionGet
Read session persistent data. It is a protected method, login is required
Returns a hash
any['post','put'] => '/session_read' => sub {
my %hash1 = SessionGet( 'k1','k2' ); # some records
my %hash2 = SessionGet( [ 'k1','k2' ] ); # some records
my %hash3 = SessionGet(); # all records
reply { %hash3 }
};
curl $url/session_read?token=17398-5c8a71b
SessionDel
Deletes session persistent data. It is a protected method, login is required
Returns a list of the deleted keys
SessionDel; delete all keys
SessionDel( 'rec1', 'rec2', ... ); delete selected keys
SessionDel( [ 'rec1', 'rec2', ... ] ); delete selected keys
any['delete'] => '/session_delete' => sub {
my $arg = UserData();
my @arr = SessionDel( $arg );
reply { 'Deleted keys' => \@arr }
};
curl -X DELETE $url/session_delete?token=17398-5c8a71b -H "$H" -d '["k1","k2","k9"]'
{
"error" : 0,
"reply" : {
"Deleted keys" : [ "k1" , "k2" ]
}
}
Authentication and role based access control
The routes can be either public or protected
- protected
-
routes that you must provide the I<token>, as returned by the I<login> route. Afer login, you can save, update, read, delete persistent session data The B<login> route is using the the first active authentication method of the I<config.yml> - public
-
routes that anyone can use without B<login> , they do not support sessions / persistent data.
Configuration file "Application dir/config.yml"
This file customize the application name, version, securrity, routes and Authentication methods. The following is an example
appname : TestService
appversion : 1.0.0
environment : development
layout : main
charset : UTF-8
template : template_toolkit
engines:
template:
template_toolkit:
EVAL_PERL : 0
start_tag : '<%'
end_tag : '%>'
plugins:
WebService:
Session enable : true
Session directory : /var/lib/WebService
Session idle timeout: 86400
Default format : json
Allowed hosts :
- "127.*"
- "172.20.20.*"
- "????:????:????:6d00:20c:29ff:*:ffa3"
- "10.*.?.*"
- "*"
Routes:
text : { Protected: false }
mirror : { Protected: false }
Protected : { Protected: true }
Protected_text_ref: { Protected: true }
list : { Protected: false }
list_ref : { Protected: false }
hash : { Protected: false }
code\/text : { Protected: false }
code\/list : { Protected: false }
code\/hash : { Protected: false }
code\/text_ref : { Protected: false }
code\/list_ref : { Protected: false }
keys_selected : { Protected: false }
git\/commit : { Protected: true, Groups: [ git , ansibleremote ] }
session_save : { Protected: true, Groups: [] }
session_read : { Protected: true, Groups: [] }
session_delete : { Protected: true, Groups: [] }
Authentication methods:
- Name : INTERNAL
Active : true
Accounts :
user1 : s3cr3T+PA55sW0rD
user2 : <any>
<any> : S3cREt-4-aLl
#<any> : <any>
- Name : Linux native users
Active : false
Command : MODULE_INSTALL_DIR/AuthScripts/Linux_native_authentication.sh
Arguments : [ ]
Use sudo : true
- Name : Basic Apache auth for simple users
Active : false
Command : MODULE_INSTALL_DIR/AuthScripts/HttpBasic.sh
Arguments : [ "/etc/htpasswd" ]
Use sudo : false
Authentication methods
Authentication method can be INTERNAL or external executable Command.
At INTERNAL you define the usernames / passwords directly at the config.yml . The <any> means any username or password, so if you want to allow all users to login no matter the username or the password use
<any> : <any>
This make sense if you just want to give anyone the ability for persistent data
The protected routes, at config.yml have Protected:true and their required groups e.g. Groups:[grp1,grp2 ...]
The user must be member to all defined groups
If the route's Groups list is empty or missing, then the groups membership is ignored
This way you can have user based access, because every user is allowed to access his assigned routes.
Authentication scripts
At production enviroments, probably you want to use external authenticators, accessed by plugable scripts e.g for the native "Linux native" authentication
MODULE_INSTALL_DIR/AuthScripts/Linux_native_authentication.sh
It is easy to write your own scripts for LDAP, Active Directory, OAuth 2.0, Keycload, etc external authenticators.
If the script needs sudo, you must add the user running the application to sudoers e.g
dendrodb ALL=(ALL:ALL) NOPASSWD: /usr/share/perl5/site_perl/Dancer2/Plugin/AuthScripts/some_auth_script.sh
Please read the file AUTHENTICATION_SCRIPTS for the details
IP access
You can control which clients are allowed to use your application at the file config.yml
The rules are checked from up to bottom until there is a match. If no rule match then the client can not login. At rules your can use the wildcard characters * ?
...
plugins:
WebService:
Allowed hosts:
- 127.*
- 10.*
- 172.20.*
- 32.??.34.4?
- 4.?.?.??
- ????:????:????:6d00:20c:29ff:*:ffa3
- 192.168.0.153
- "*"
Sessions
Upon successful login, the client is in session until logout or its session expired due to inactivity.
While in session you can access protected routes and save, read, delete session persistent data.
at the config.yml You can change persistent data storage directory and session expiration
- Storage directory
-
Be careful this directory must be writable from the user that is running the service To set the sessions directory plugins: WebService: Session directory : /var/lib/WebService or at your application setting('plugins')->{'WebService'}->{'Session directory'} = '/var/lib/WebService'; - Session expiration
-
Sessions are expiring after some seconds of inactivity. You can change the amount of seconds either at the I<config.yml> plugins: WebService: Session idle timeout : 3600 or at your application setting('plugins')->{'WebService'}->{'Session idle timeout'} = 3600;
Built in plugin routes
These are plugin built in routes
WebService version
WebService/client client propertis
WebService/routes list the built-in and application routes
login login
logout logout
Usage examples
export url=http://127.0.0.1:3000 H="Content-Type: application/json"
alias curl="$(/usr/bin/which curl) --silent --user-agent Perl"
curl $url/WebService
curl $url/WebService/client
curl $url/WebService/routes?sort=true
curl "$url/WebService?to=json&pretty=true&sort=true"
curl $url/WebService?to=yaml
curl "$url/WebService?to=xml&pretty=false"
curl "$url/WebService?to=xml&pretty=true"
curl $url/WebService?to=human
curl $url/WebService?to=perl
curl $url
Application routes
Based on the code of our TestService ( lib/TestService.pm ) some examples of how to login, logout, and route usage
curl "$url/mirror?from=json&to=json&k1=a&k2=b" -d '{"k1" : ["one","two","three"]}'
curl "$url/mirror?to=xml&pretty=true" -d '{"k1" : ["one","two","three"]}'
curl "$url/mirror?from=yaml&to=perl" -d '"k1" : ["one","two","three"]'
curl "$url/mirror?from=xml&to=yaml" -d '<root><k1>one</k1><k2>two</k2></root>'
Login
curl -X POST $url/login -H "$H" -d '{"username": "user1", "password": "s3cr3T+PA55sW0rD"}'
Protected application routes
curl $url/text
curl $url/text?token=17393926-5c8-0
curl $url/session_save?token=17393926-5c8-0 -H "$H" -X POST -d '{"k1":"v1", "k2":"v2", "k3":"v3"}'
curl $url/session_read?token=17393926-5c8-0
curl $url/session_delete?token=17393926-5c8-0 -H "$H" -X DELETE -d '["k3","k8","k9"]'
curl $url/session_read?token=17393926-5c8-0
Logout
curl $url/logout?token=17393926-5c8-0
curl $url/logout -d '{"token":"17393926-5c8-0"}' -H "$H" -X POST
Plugin Installation
You should your run your APIs as a non privileged user e.g. the "dancer"
getent group dancer >/dev/null || groupadd dancer
getent passwd dancer >/dev/null || useradd -g dancer -l -m -c "Dancer2 WebService" -s $(which nologin) dancer
i=/var/lib/WebService; [ -d $i ] || { mkdir $i; chown -R dancer:dancer $i; }
i=/var/log/WebService; [ -d $i ] || { mkdir $i; chown -R dancer:dancer $i; }
cpanm Dancer2
cpanm Dancer2::Plugin::WebService
Create a sample application e.g. the "TestService"
Follow the CREATE_SAMPLE_APPLICATION document to create the sample application TestService
Start the application
To start it manual as user dancer from the command line
- Production
-
sudo -u dancer plackup --host 0.0.0.0 --port 3000 --server Starman --workers=5 --env development -a /home/dancer/TestService/bin/app.psgi - While developing
-
sudo -u dancer plackup --host 0.0.0.0 --port 3000 --env development --app /home/dancer/TestService/bin/app.psgi --server HTTP::Server::PSGI
view also the INSTALL document for details
Configure the loggger at the environment file
Application dir/environments/[development|production].yml
log : "debug" # core, debug, info, warning, error
show_stacktrace : 0
no_server_tokens : 1
warnings : 1 # should Dancer2 consider warnings as critical errors?
show_errors : 1 # if true shows a detailed debug error page , otherse the views/404.tt or public/404.html
startup_info : 1 # print the banner
no_server_tokens : 1 # disable server tokens in production environments
logger : "file" # console: to STDOUT , file:to file
engines :
logger :
file :
log_format : '{"ts":"%T","host":"%h","pid":"%P","message":"%m"}'
log_dir : "/tmp"
file_name : "test.log"
See also
Plack::Middleware::REST Route PSGI requests for RESTful web applications
Dancer2::Plugin::REST A plugin for writing RESTful apps with Dancer2
RPC::pServer Perl extension for writing pRPC servers
RPC::Any A simple, unified interface to XML-RPC and JSON-RPC
XML::RPC Pure Perl implementation for an XML-RPC client and server.
JSON::RPC JSON RPC Server Implementation
AUTHOR
George Bouras <george.mpouras@yandex.com>
COPYRIGHT AND LICENSE
This software is copyright (c) 2025 by George Bouras.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.