NAME
Dancer2::Plugin::WebService - Rapid creation of REST web services with sessions and persistent data
VERSION
version 3.010
NAME
Dancer2::Plugin::WebService Rapid creation of REST web services with sessions and persistent data
VERSION
version 3.010
SYNOPSIS
Using curl ( https://curl.haxx.se ) as client. At your main script you can use all the Dancer2 core methods.
All replies have the extra keys error and errormessage . At success error will be 0 . At fail the error will be non 0 while the errormessage will contain a description of the error
curl localhost:65535/info/version
curl localhost:65535/info/version?to=yaml
curl localhost:65535/info/version?to=perl
curl -X GET localhost:65535/info/version?to=human
curl localhost:65535/info/client
curl localhost:65535/info/client?to=json
curl localhost:65535/info/client?to=xml
curl --data '{"k1":"v1", "k2":"v2"}' localhost:65535/test_mirror
curl --data '{"k1":"v1", "k2":"v2"}' 'localhost:65535/test_mirror?to=xml'
curl --data '<D><k1>v1</k1></D>' 'localhost:65535/test_mirror?from=xml;to=human'
curl --data '{"k1":"v1", "k2":"v2"}' localhost:65535/test_get_one_key
curl --data '{"k1":"3", "k2":"5" }' localhost:65535/test_get_data
curl --data '{"k1":"v1", "k2":"v2"}' localhost:65535/test_new_data
curl --data '{"k1":"v1", "k2":"v2"}' localhost:65535/test_session
curl --data '{"user":"joe", "password":"password"}' localhost:65535/login
curl --data '{"k1":"v1", "k2":"v2", "SessionID":"13d11def280e37ec2d7fbeca096e7d83"}' localhost:65535/test_session
curl --data '{"SessionID" : "13d11def280e37ec2d7fbeca096e7d83"}' localhost:65535/logout
Your script (example)
package TestService;
use strict;
use warnings;
use Dancer2;
use Dancer2::Plugin::WebService;
our $VERSION = setting('plugins')->{WebService}->{Version};
any '/test_mirror' => sub { RestReply('DATA_USER_SEND') };
any '/test_get_one_key' => sub { RestReply('k1') };
any '/test_get_data' => sub {
my ($var1, $var2) = get_data_user('k1', 'k2');
RestReply( Total => ($var1 + $var2), Thought => 'Lets add !' )
};
any '/test_new_data' => sub {
my %data =
set_data_user( new1 => 'N1', new2 => 'N2' );
set_data_user( new3 => 'N3', new4 => 'N4' );
del_data_user( 'new1' , 'new4' );
RestReply('DATA_USER_ALL')
};
setting('plugins')->{'WebService'}->{'Routes'}->{'test_session'} = 'private';
any '/test_session' => sub {
my ($v1, $v2) = get_data_user('k1', 'k2');
set_data_session(s1 =>'L1', s2=>'L2', s3=>['L3a', 'L3b']);
del_data_session('s7', 's8');
my @Some = get_data_session('s1', 's2', 's3', 's7');
my %All = get_data_session();
RestReply(k1=>$v1, k2=>$v2, SesData_A => $Some[2], SesData_b=> [ @Some[0..1] ], SesData_all=> { %All } )
};
dance;
POLYMORPHISM
Dancer2::Plugin::WebService can handle as input or output multiple formats
json
xml
yaml
perl
human
Define input/output format using the url parameters "to" and "from". If missing the default is json. The "to" is the same as "from" if missing. e.g.
curl localhost:65535/info/client?to=json
curl localhost:65535/info/client?to=xml
curl localhost:65535/info/client?to=yaml
curl localhost:65535/info/client?to=perl
curl localhost:65535/info/client?to=human
curl --data '{"k1":"3", "k2":"30"}' localhost:65535/test_get_data
curl --data '{"k1":"3", "k2":"30"}' 'localhost:65535/test_get_data?to=xml'
curl --data '{"k1":"3", "k2":"30"}' 'localhost:65535/test_get_data?to=yaml'
curl --data '{"k1":"3", "k2":"30"}' 'localhost:65535/test_get_data?to=perl'
curl --data '{"k1":"3", "k2":"30"}' 'localhost:65535/test_get_data?from=json;to=human'
curl --data '<Data><k1>3</k1><k2>30</k2></Data>' 'localhost:65535/test_get_data?from=xml'
curl --data '<Data><k1>3</k1><k2>30</k2></Data>' 'localhost:65535/test_get_data?from=xml;to=human'
curl --data '<Data><k1>3</k1><k2>30</k2></Data>' 'localhost:65535/test_get_data?from=xml;to=yaml'
ROUTES
Your routes can be either public or private
public are the routes that anyone can use freely without login , they do not support persistent data, so you if you want to pass some data you must send them.
private are the routes that they need user to login . At private routes you can have read/write/delete/update persistent data. These data are automatic deleted when you logout. Methods get_data_session , set_data_session , del_data_session can be used only at private routes
You can flag a route as private either at the config.yml
plugins:
WebService:
Routes:
SomeRoute: private
or at your main script
setting('plugins')->{'WebService'}->{'Routes'}->{'SomeRoute'} = 'private';
BUILT-IN ROUTES
There are some built in routes for your convenience. You can use the "from" and "to" format modifiers if you want
info/version
Service information (public route)
curl localhost:65535/info/version
curl localhost:65535/info/version?to=yaml
curl localhost:65535/info/version?to=xml
curl localhost:65535/info/version?to=perl
curl localhost:65535/info/version?to=human
info/client
Client information (public route)
curl localhost:65535/info/client
info
Redirects to /info/version
login
Login for using private routes and storing persistent data. It is a public route.
curl -s --data '{"user":"joe","password":"password"}' 'http://localhost:65535/login'
You can control which clients are allowed to login by editing the file config.yml
plugins:
WebService:
Allowed hosts:
- 127.*
- 10.*
- 192.168.1.23
- 172.20.*
- 32.??.34.4?
- 4.?.?.??
- ????:????:????:6d00:20c:29ff:*:ffa3
- "*"
logout
It is private route as you can not logout without login . In order to logout you must know the SessionID . If you logout you can not use the private routes and all coresponded session data are deleted.
curl -X GET --data '{"SessionID":"0a1ad34505076d930c3f76c52645e54b"}' localhost:65535/logout
curl -X GET --data '{"SessionID":"0a8e4f0523dafa980ec35bcf29a5cc8c"}' 'localhost:65535/logout?from=json;to=xml'
SESSIONS
The sessions auto expired after some seconds of inactivity. You can change the amount of seconds either at the config.yml
plugins:
WebService:
Session idle timout : 3600
or at your main script
setting('plugins')->{'WebService'}->{'Session idle timout'} = 3600;
You can change Session persistent data storage directory at the config.yml
plugins:
WebService:
Session directory : /usr/local/sessions
METHODS
RestReply
send the reply to the client. This should be the last route's statement
RestReply only the error and the errormessage
RestReply( k1 => 'v1', ... ) anything you want
RestReply( { k1 => 'v1', ... } ) anything you want
RestReply('DATA_USER_SEND') data send by the user
RestReply('DATA_USER_ALL') data send by the user with any addtions
RestReply('k1') data send by the user only one key
get_data_user
Retrieves data user send to WebService with his client e.g. curl or wget . We use this to do something usefull with user's data. This is normally the method you will use more often
my ($var1, $var2) = get_data_user('k1', 'k2'); # return the selected keys
my %hash = get_data_user(); # return all data as hash
set_data_user
You can define extra data. Try to use this instead of common $variables , @arrays , etc because they are visible to other users of the service !
my %data = set_data_user( new1 => 'foo1', new2 => 'foo2' ); # return the keys
my %data = set_data_user( { new1 => 'foo1', new2 => 'foo2' } ); # return the keys
del_data_user
Deletes data, think it as the opposite of set_data_user
del_data_user( 'k1', 'k2', ... ); # delete only the selected keys
del_data_user(); # delete all keys
set_data_session
Store persistent session data. Session data are not volatile like the user data between service calls. You have to login prior using this method.
set_data_session( new1 => 'foo1', new2 => 'foo2' );
set_data_session( { new1 => 'foo1', new2 => 'foo2' } );
get_data_session
Retrieves session data. You have to login prior using this method.
my %data = get_data_session( 'k1', 'k2', ... ); # return only the selected keys
my %data = get_data_session(); # returs all keys
del_data_session
Deletes session data. You have to login prior using this method.
del_data_session( 'k1', 'k2', ... ); # deletes only the selected keys
del_data_session(); # deletes all keys
INSTALLATION
After install Dancer2::Plugin::WebService create your application e.g TestService using the command inside e.g. the /opt folder
cd /opt
dancer2 gen --application TestService
Assuming that you will start the service as a non privileged user e.g. joe
mkdir /var/log/TestService
mkdir /usr/local/sessions
chown -R joe:joe /opt/TestService
chown -R joe:joe /var/log/TestService
chown -R joe:joe /usr/local/sessions
If you want compressed replies edit the file /opt/TestService/bin/app.psgi
#!/usr/bin/perl
use FindBin;
use lib "$FindBin::Bin/../lib";
use TestService;
use Plack::Builder;
builder { enable 'Deflater'; TestService->to_app }
Or if you have slow CPU use uncompressed replies by editing the file /opt/TestService/bin/app.psgi
#!/usr/bin/perl
use FindBin;
use lib "$FindBin::Bin/../lib";
use TestService;
TestService->to_app;
Edit the file .../environments/production.yml
show_errors : 1
startup_info : 1
warnings : 1
no_server_tokens : 0
log : "core"
logger : "file"
engines :
logger :
File :
log_dir : "/var/log/TestService"
file_name : "activity.log"
Edit the file /opt/TestService/config.yml
appname : TestService
environment : production
plugins:
WebService:
Version : 1.0.0
Owner : Joe Lunchbucket, Joe.Lunchbucket@example.com
Session directory : /usr/local/sessions
Session idle timout : 3600
Default format : json
Command sudo : /usr/bin/sudo
Command rm : /usr/bin/rm
Routes :
test1 : public
test2 : public
test3 : private
Allowed hosts:
- 127.*
- 10.*
- 192.168.1.23
- 172.20.*
- 32.??.34.4?
- 4.?.?.??
- ????:????:????:6d00:20c:29ff:*:ffa3
- "*"
User must belong to one or more of the groups:
- power
- storage
- network
Authentication method:
Always allow login for testing:
Command : MODULE_INSTALL_DIR/scripts/AlwaysOk/AlwaysOk.sh
Active : no
Use sudo : no
Linux native users:
Command : MODULE_INSTALL_DIR/scripts/LinuxOS/AuthUser.pl
Active : yes
Use sudo : yes
Basic Apache auth for simple users:
Command : MODULE_INSTALL_DIR/scripts/HttpBasic/users.pl
Active : no
Use sudo : no
Basic Apache auth for admins:
Command : MODULE_INSTALL_DIR/scripts/HttpBasic/admins.pl
Active : no
Use sudo : no
Active directory:
Command : MODULE_INSTALL_DIR/scripts/ActiveDirectory/ActiveDirectory.pl
Active : no
Use sudo : no
LDAP:
Command : MODULE_INSTALL_DIR/scripts/LDAP/LDAP.pl
Active : no
Use sudo : no
Write your code at the file /opt/TestService/lib/TestService.pm
e.g.
package TestService;
use strict;
use warnings;
use Dancer2;
use Dancer2::Plugin::WebService;
our $VERSION = setting('plugins')->{WebService}->{Version};
any '/test_mirror' => sub { RestReply('DATA_USER_SEND') };
any '/test_get_one_key' => sub { RestReply('k1') };
any '/test_get_data' => sub {
my ($var1, $var2) = get_data_user('k1', 'k2');
RestReply( Total => ($var1 + $var2), Thought => 'Lets add !' )
};
any '/test_new_data' => sub {
my %data =
set_data_user( new1 => 'N1', new2 => 'N2' );
set_data_user( new3 => 'N3', new4 => 'N4' );
del_data_user( 'new1' , 'new4' );
RestReply('DATA_USER_ALL')
};
setting('plugins')->{'WebService'}->{'Routes'}->{'test_session'} = 'private';
any '/test_session' => sub {
my ($v1, $v2) = get_data_user('k1', 'k2');
set_data_session(s1 =>'L1', s2=>'L2', s3=>['L3a', 'L3b']);
del_data_session('s7', 's8');
my @Some = get_data_session('s1', 's2', 's3', 's7');
my %All = get_data_session();
RestReply(k1=>$v1, k2=>$v2, SesData_A => $Some[2], SesData_b=> [ @Some[0..1] ], SesData_all=> { %All } )
};
dance;
Start the service as user Joe listening at port e.g 65535 with the real IP for production and multiple threads
sudo -u joe /usr/bin/site_perl/plackup --host 172.20.21.20 --port 65535 --server Starman --workers=10 --env production -a /opt/TestService/bin/app.psgi
or during development
sudo -u joe /usr/bin/site_perl/plackup --port 65535 -a /opt/TestService/bin/app.psgi --Reload /opt/TestService/lib,/opt/TestService/config.yml,/usr/share/perl5/site_perl/Dancer2/Plugin
sudo -u joe /usr/bin/site_perl/plackup --port 65535 -a /opt/TestService/bin/app.psgi
or without Plack
sudo -u joe perl /opt/TestService/bin/app.psgi
if you want to install your WebService application as Linux service please readme the INSTALL
AUTHENTICATION
For using private methods and persistent session data you have to login. login is handled from external scripts/programs . It is very easy to write your own script to have authentication with anything you can imagine. The authentication is configured at config.xml Only one authentication method can ne active at any time. The authentication scripts must be executable from the user running the service. Some scripts need elevated privileges so you need to enable sudo for them. E.g. for the native Linux mechanism we have
User must belong to one or more of the groups:
- power
- storage
- network
Authentication method:
Linux native users:
Command : MODULE_INSTALL_DIR/scripts/LinuxOS/AuthUser.pl
Active : yes
Use sudo : yes
That means that if a user do not belong to any of the groups power , storage , network the login will fail . We want to use thie method so we have Active : yes . Also because this work only with root priviliges we have Use sudo : yes
That means if you running the service as user joe , at the file /etc/sudoers must exist a line similar to
joe ALL=NOPASSWD: /usr/share/perl5/site_perl/Dancer2/Plugin/scripts/LinuxOS/AuthUser.pl
For writing your own script please read the scripts howto.txt and review the existing scripts
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 2.0 Server Implementation
COPYRIGHT AND LICENSE
This software is copyright (c) 2017 by George Bouras
It is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
AUTHOR
George Bouras <george.mpouras@yandex.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2017 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.