Dancer2::Plugin::WebService - RESTful Web Services with login, persistent data, multiple in/out formats, IP security, role based access
version 4.2.6
The replies through this module have the extra key error . At success error is 0 , while at fail is the error description
GET WebService GET WebService/client GET WebService/about GET WebService/version
POST AllKeys?to=yaml posted data {"k1":"v1"} POST SomeKeys?to=xml posted data {"k1":"v1"} POST login posted data {"username":"joe", "password":"souvlaki"} POST LoginNeeded_store posted data {"token":"2d85b82b158e", "k1":"v1", "k2":"v2"} POST LoginNeeded_delete posted data {"token":"2d85b82b158e"} POST LoginNeeded_read posted data {"token":"2d85b82b158e"} POST logout posted data {"token":"2d85b82b158e"}
package MyService; use Dancer2; use Dancer2::Plugin::WebService; post '/AllKeys' => sub { reply PosteData }; post '/SomeKeys' => sub { reply PosteData('k1','k2') }; get '/data1' => sub { reply 'k1'=>'v1','k2'=>'v2' }; get '/data2' => sub { reply {'k1'=>'v1','k2'=>'v2'}}; any '/data3' => sub { my %H = PosteData('k1', 'k2'); reply 'foo'=> $H{k1}, 'boo'=>$H{k2} }; get '/error' => sub { reply 'k1', 'v1', 'error', 'oups' }; any '/LoginNeeded_store' => sub { reply session_set('s1'=>'sv1', 's2'=>'v1') }; post '/LoginNeeded_delete'=> sub { reply session_del('s1', 's2') }; any '/LoginNeeded_read' => sub { reply session_get('s1', 's2') }; dance;
url parameters to control the reply
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
from , to define the input/output format. You can mix input/output formats independently. Supported formats are
json xml yaml perl human
from default is the config.yml property
plugins : WebService : Default format : json
GET /SomeRoute?to=human&sort=true&pretty=true GET /SomeRoute?to=perl&sort=true&pretty=false POST /SomeRoute?to=xml&sort=true' {"k1":"v1"} POST /SomeRoute?to=yaml' {"k1":"v1"} POST /SomeRoute?to=perl' {"k1":"v1"} POST /SomeRoute?from=json;to=human' {"k1":"v1"} POST /SomeRoute?from=xml;to=human' <Data><k1>v1</k1></Data> POST /SomeRoute?from=xml;to=yaml' <Data><k1>v1</k1></Data>
Your routes can be either public or protected
public are the routes that anyone can use without login , Τhey do not support sessions / persistent data, but you can post and access data using the method PosteData
protected are the routes that you must provide a token, returned by the login route. At protected routes you can read, write, delete persistent data using the methods session_get , session_set , session_del
Persistent session data are auto deleted when you logout or if your session expired.
You can define a route as protected at the config.yml
plugins: WebService: Routes: SomeRoute: protected
or at your application code
setting('plugins')->{'WebService'}->{'Routes'}->{'SomeRoute'} = 'protected';
public informational routes
You can use "to" format modifiers if you want
GET /WebService The available routes GET /WebService/about About GET /WebService/version Perl, Dancer2, WebService, apllication version GET /WebService/client Your client information
public route
Login to get a token for using protected routes and storing persistent data
POST /login {"username":"SomeUser","password":"SomePass"} e.g. curl -X POST 0/login -d '{"username":"jonathan","password":"__1453__"}'
protected route
If you logout your session and all your persistent data are deleted
POST logout posted data {"token":"SomeToken"} e.g curl -X POST 0/logout --data '{"token":"a105076d9"}'
You can control which clients IP addresses are allowed to login by editing 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.* - 192.168.1.23 - 172.20.* - 32.??.34.4? - 4.?.?.?? - ????:????:????:6d00:20c:29ff:*:ffa3 - "*"
Upon successful login, client is in session until logout or get expired due to inactivity. In session you can use the session methods by providing the token you received.
You can change persistent data storage directory at the config.yml
plugins: WebService: Session directory : /var/lib/WebService
or at your main script
setting('plugins')->{'WebService'}->{'Session directory'} = '/var/lib/WebService';
Be careful this directory must be writable from the user that is running the service
Sessions expired after some seconds of inactivity. You can change the amount of seconds either at the config.yml
plugins: WebService: Session idle timeout : 3600
setting('plugins')->{'WebService'}->{'Session idle timeout'} = 3600;
WebService methods for your main Dancer2 code
public method
Send the reply to the client; it applies any necessary format convertions. This should be the last route's statement
reply only the error reply k1 => 'v1', ... anything you want reply( { k1 => 'v1', ... } ) anything you want reply 'k1' The specific key and its value of the posted data
Get the data posted by the user
PosteData Everything posted my %hash = PosteData('k2','k4'); Only some keys of a hash my @list = PosteData('k2','k4'); Only some items of a list
The posted data can be anything; hashes, lists, scalars
curl -X POST 0:/ -d '{ "k1":"v1", "k2":"v2", "k3":"v3" }' curl -X POST 0:/ -d '[ "k1", "k2", "k3", "k4" ]'
session method
Read session persistent data
my %data = session_get; returns a hash of all keys my %data = session_get( 'k1', 'k2', ... ); returns a hash of the selected keys my %data = session_get(['k1', 'k2', ... ]); returns a hash of the selected keys
Store non volatile session persistent data;
You must pass your data as key / value pairs
session_set( 'rec1' => 'v1', 'rec2' => 'v2', ... ); session_set( { 'rec1' => 'v1', 'rec2' => 'v2', ... } );
It returns a document of the stored keys, your can use the url to=... modifier e.g.
{ "error" : 0, "stored keys" : [ "rec1", "rec2" ] }
Deletes session persistent data
session_del; delete all keys session_del( 'rec1', 'rec2', ... ); delete selected keys session_del( [ 'rec1', 'rec2', ... ] ); delete selected keys
It returns a document of the deleted keys, your can use the url to=... modifier e.g.
{ "error" : 0, "deleted keys" : [ "rec1", "rec2" ] }
For using protected routes, you must provide a valid token received from the login route. The login route is using the the first active authentication method of the config.yml 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
At production enviroments, probably you want to use an external auth script e.g for the native Linux authentication
MODULE_INSTALL_DIR/AuthScripts/linux.sh
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 the route Groups otherelse the route will not run.
If the route's Groups list is empty or missing, the route will run with any valid token ignoring the user's group membership.
This is usefull because you can have role based access control at your routes. Every user with its token will be able to access only the routes is assigned to.
A sample route definition. Plese mention the \/ path separator
Routes: Route1 : Protected : false Route\/foo1 : Protected : true Groups : [ group1 , group2 ... ] Route\/foo2 : Protected : true Groups : [ ]
It is easy to write your own scripts for Active Directory, LDAP, facebook integration or whatever.
If the Command needs sudo, you must add the user running the WebService to sudoers
Please read the AUTHENTICATION_SCRIPTS for the details
A sample config.yml is the following.
version : 3.0.0 environment : development plugins : WebService : Default format : json Session directory : /var/lib/WebService Session idle timeout: 86400 Routes : mirror : { Protected: false } somekeys : { Protected: false } data\/m1 : { Protected: false } data\/m1 : { Protected: false } INeedLogin_store : { Protected: true, Groups: [ ftp , storage ] } INeedLogin_delete : { Protected: true, Groups: log } INeedLogin_read : { Protected: true } Allowed hosts: - 127.* - 10.* - 172.16.?.* - 192.168.1.23 - "????:????:????:6d00:20c:29ff:*:ffa3" - "*" Authentication methods: - Name : INTERNAL Active : true Accounts : user1 : pass1 user2 : <any> <any> : Secret4All - Name : Linux users Active : false Command : MODULE_INSTALL_DIR/AuthScripts/linux.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
You should run your service a non privileged user e.g. dancer
Create your application ( TestService ) e.g. at /opt/TestService/
dancer2 gen --application TestService --directory TestService --path /opt --overwrite chown -R dancer:dancer /opt/TestService
Write your code at the file /opt/TestService/lib/TestService.pm
/opt/TestService/environments/development.yml
# logger : file, console # log level : core, debug, info, warning, error startup_info : 1 show_errors : 1 warnings : 1 no_server_tokens : 0 log : 'core' logger : 'console' engines: logger: file: log_format : '{"ts":"%{%Y-%m-%d %H:%M:%S}t","host":"%h","level":"%L","message":"%m"}' log_dir : '/var/log/WebService' file_name : 'TestService.log' console: log_format : '{"ts":"%{%Y-%m-%d %H:%M:%S}t","host":"%h","level":"%L","message":"%m"}'
Start the service as user dancer
plackup --host 0.0.0.0 --port 3000 --env production --server Starman --workers=5 -a /opt/TestService/bin/app.psgi plackup --host 0.0.0.0 --port 3000 --env development --server HTTP::Server::PSGI --Reload /opt/TestService/lib/TestService.pm,/opt/TestService/config.yml -a /opt/TestService/bin/app.psgi plackup --host 0.0.0.0 --port 3000 -a /opt/TestService/bin/app.psgi # without Plack perl /opt/TestService/bin/app.psgi
view the INSTALL document for details
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
George Bouras <george.mpouras@yandex.com>
This software is copyright (c) 2019 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.
To install Dancer2::Plugin::WebService, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Dancer2::Plugin::WebService
CPAN shell
perl -MCPAN -e shell install Dancer2::Plugin::WebService
For more information on module installation, please visit the detailed CPAN module installation guide.