Dancer2-Plugin-WebService
view release on metacpan or search on metacpan
CREATE_SAMPLE_APPLICATION view on Meta::CPAN
console :
log_format : '{"ts":"%T","host":"%h","pid":"%P","message":"%m"}'
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"
Configure the development enviroment at
/home/dancer/TestService/environments/development.yml
logger : console
log : debug
warnings : 1
show_errors : 1
startup_info : 1
show_stacktrace : 1
no_server_tokens : 0
show_stacktrace : 1
engines :
logger :
console :
log_format : '{"ts":"%T","host":"%h","pid":"%P","message":"%m"}'
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"
Write your TestService code at the file /home/dancer/TestService/lib/TestService.pm
package TestService;
use strict;
use warnings;
use Dancer2;
use Dancer2::Plugin::WebService;
set no_default_middleware => true;
our $VERSION = exists config->{appversion} ? config->{appversion} : '0.0.0.0';
my $arg;
# Wellcome & help screen (classic html)
get '/' => sub{send_as html => template 'index' => {'title' => 'TestService'}, {layout=>'main'}};
any[ 'get', 'post', 'put' ] => '/mirror' => sub { reply UserData };
get '/text' => sub { reply 'hello world' }; # text
get '/list' => sub { Error('ok'); reply 'a', 'b', 'c' }; # array
get '/list_ref' => sub { Error('Oups'); reply [ 'a', 'b' ] }; # array reference
get '/hash' => sub { reply {'κλειδί'=>['Îνα','δÏο','v3'] } }; # hash
get '/code/text' => sub { reply sub { 'hello world' } }; # Code text
get '/code/list' => sub { reply sub { 'a', 'b', 'c', 'd' } }; # Code array
get '/code/hash' => sub { reply sub { {'k1'=>'v1','k2'=>'v2'} } }; # Code hash
get '/code/text_ref' => sub { reply sub { \'hello world' } }; # Code text reference
get '/code/list_ref' => sub { reply sub { [ 'a', 'b', 'c', 'd' ] } }; # Code list reference
get '/code/list_ref' => sub { reply sub { [ 'a', 'b', 'c', 'd' ] } }; # Code list reference
any['get','post','put'] => '/keys_selected' => sub { reply UserData('k1','k2') };
any['get','post'] => '/Protected' => sub { Error('ÏÏα'); reply UserData };
get '/Protected_text_ref' => sub { Error('ok'); reply \'hello world' }; # text reference
get '/project/commit' => sub { reply 'commited' };
# UniCode tests
any['get','post']=> '/unicode_test_mirror' => sub { reply UserData() };
any['get','post']=> '/unicode_test_session_set' => sub { reply SessionSet( UserData ) };
any['delete'] => '/unicode_test_session_del' => sub { reply SessionDel( UserData ) };
get '/unicode_test_session_get' => sub { reply { SessionGet( UserData ) } };
# Store persistent data
# SessionSet( { k1=>'v1' , k2=>'v2' } );
# SessionSet( k1=>'v1' , k2=>'v2' );
any['get','post','put'] => '/session_save' => sub {
my $arg = UserData();
my @arr = SessionSet( $arg );
reply { 'saved keys' => \@arr }
};
# Retrieve persistent data. returns a normal hash
# SessionGet( 'k1','k2' ); # some records
# SessionGet( [ 'k1','k2' ] ); # some records
any['post','put','get'] => '/session_read' => sub {
my %hash = SessionGet(); # all reocords
reply { %hash }
};
# Delete persistent data
# SessionDel(); # All records
# SessionDel( 'k1', 'k2', ...); # Some records
any['delete'] => '/session_delete' => sub {
my $arg = UserData();
my @arr = SessionDel( $arg );
reply { 'Deleted keys' => \@arr }
};
dance;
Define the routes, their security and the authentication method at the file /home/dancer/TestService/config.yml
appname : TestService
appversion : 1.0.0
environment : development
layout : main
charset : UTF-8
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:
unicode_test_mirror : { Protected: false }
unicode_test_session_set : { Protected: false }
unicode_test_session_get : { Protected: false }
unicode_test_session_del : { Protected: false }
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 }
project\/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
Test the custom routes.
Start the TestService manual from the command line.
As first step we must Define the plackup fullpath
plackup=$(/usr/bin/find /usr/bin -name plackup -type f)
[ -x "$plackup" ] && "$plackup" --version || echo Could not found the plackup utility. Check the INSTALL document and try again
# For development using the HTTP::Server::PSGI or HTTP::Server::Simple
sudo -u dancer $plackup --host 0.0.0.0 --port 3000 --env development --app /home/dancer/TestService/bin/app.psgi --server HTTP::Server::PSGI
sudo -u dancer $plackup --host 0.0.0.0 --port 3000 --env development --app /home/dancer/TestService/bin/app.psgi --server HTTP::Server::Simple --Reload /home/dancer/TestService/lib/TestService.pm,/home/dancer/TestService/config.yml,/opt/Dancer2-Plu...
# For production using the Starman
sudo -u dancer $plackup --host 0.0.0.0 --port 3000 --env production --app /home/dancer/TestService/bin/app.psgi --server Starman --workers=5
# As console application
sudo -u dancer /usr/bin/perl -I lib /home/dancer/TestService/bin/app.psgi
Open an other UTF8 console and export the url and header variables
CREATE_SAMPLE_APPLICATION view on Meta::CPAN
curl "$url/mirror?from=xml&to=human" -d '<root><κλειδί1>Îνα</κλειδί1><κλειδί2>δÏο</κλειδί2></root>'
User function "Mirror" using perl data
curl "$url/mirror?from=perl&to=json" -d '{ "Îλειδί" => ["Îνα","δÏο","three"] }'
curl "$url/mirror?from=perl&to=yaml" -d '{ "Îλειδί" => ["Îνα","δÏο","three"] }'
curl "$url/mirror?from=perl&to=xml" -d '{ "Îλειδί" => ["Îνα","δÏο","three"] }'
curl "$url/mirror?from=perl&to=perl" -d '{ "Îλειδί" => ["Îνα","δÏο","three"] }'
curl "$url/mirror?from=perl&to=human" -d '{ "Îλειδί" => ["Îνα","δÏο","three"] }'
User function "Mirror" using human data
curl "$url/mirror?from=human&to=json" -d 'reply.root.Îλειδί = Τιμή'
curl "$url/mirror?from=human&to=yaml" -d 'reply.root.Îλειδί = Τιμή'
curl "$url/mirror?from=human&to=xml&pretty=true" -d 'reply.root.Îλειδί = Τιμή'
curl "$url/mirror?from=human&to=perl" -d 'reply.root.Îλειδί = Τιμή'
curl "$url/mirror?from=human&to=human" -d 'reply.root.Îλειδί = Τιμή'
More user functions
curl $url/text
curl $url/list?pretty=false
curl $url/list_ref?to=yaml
curl $url/list_ref
curl $url/hash
curl $url/code/text
curl $url/code/list
curl $url/code/hash
curl $url/code/text_ref
curl $url/code/list_ref
curl $url/keys_selected?to=yaml -d '{ "k1":"v1", "k2":"v2", "k3":"v3" }'
curl $url/keys_selected?to=yaml -d '[ "k1", "k2", "k3", "k4" ]'
The protected routes needs an authorization token
Login to obtain a valid token
curl -X POST $url/login -H "$H" -d '{"username": "joe", "password": "SomePassword"}'
export token=1767562075-fe4e621515a63a7df0608630-0
curl $url/git/commit?token=$token
curl $url/Protected
curl $url/Protected?token=$token -H "$H" -d '{ "k1" : "v1" }'
curl $url/Protected -X POST -H "$H" -d "{ \"token\":\"$token\", \"k1\":\"v1\" }"
curl $url/Protected_text_ref
curl $url/Protected_text_ref?token=$token
Store persistent data
curl $url/session_save?token=$token -H "$H" -X POST -d '{ "Latin":"Hello world", "Îλληνικά":"Îειά ÏοÏ
κÏÏμε", "íêµ":"ìë
ì¸ê³" }'
curl $url/session_read?token=$token -H "$H" -X POST -d '[ "Latin", "Îλληνικά", "íêµ" ]'
curl $url/session_read?token=$token
curl $url/session_delete?token=$token -H "$H" -X DELETE -d '[ "Latin", "Îλληνικά", "íêµ" ]'
curl $url/logout?token=$token
curl $url/logout -H "$H" -X POST -d "{\"token\":\"$token\"}"
Unicode
Test if the unicode posted or replied data are handled successufully. Login is required
curl -X POST $url/unicode_test_mirror -H "$H" -d '{"Latin":"Hello world", "Îλληνικά":"Îειά ÏοÏ
κÏÏμε", "íêµ":"ìë
ì¸ê³"}'
curl -X POST "$url/unicode_test_mirror?from=xml&to=xml" -H "HXML" -d '<?xml version="1.0" encoding="UTF-8"?><root><Latin>hello world</Latin><Îλληνικά>Îειά ÏοÏ
κÏÏμε</Îλληνικά><íêµ>ìë
ì¸ê³</íêµ></root>'
curl -X POST $url/login -H "$H" -d '{"username":"joe", "password":"SomePassword"}'
export token=1767608823-2166cefaeba07576c7989e58-0
curl -X POST $url/unicode_test_session_set?token=$token -H "$H" -d '{ "Latin":"Hello world", "Îλληνικά":"Îειά ÏοÏ
κÏÏμε", "íêµ":"ìë
ì¸ê³" }'
curl -X GET $url/unicode_test_session_get?token=$token -H "$H" -d '[ "Latin", "Îλληνικά", "íêµ" ]'
curl -X DELETE $url/unicode_test_session_del?token=$token -H "$H" -d '[ "Latin", "Îλληνικά", "íêµ" ]'
curl -X POST $url/logout -H "$H" -d "{\"token\":\"$token\"}"
To start your application as Linux service
If you plan to use a reverse proxy change the listening IP (BIND) from 0.0.0.0 to 127.0.0.1
Locate the plackup with "find /usr/bin -name plackup -type f" e.g /usr/bin/site_perl/plackup
Edit/create the file /etc/systemd/system/TestService.service
[Unit]
Description=TestService rest API
Documentation=https://metacpan.org/pod/Dancer2
After=network.target
ConditionPathExists=/usr/bin/site_perl/plackup
[Service]
Type=simple
User=dancer
Group=dancer
Environment=BIND=0.0.0.0
Environment=PORT=3000
Environment=WORKERS=10
Environment=ENVIROMENT=development
ExecStart=/usr/bin/site_perl/plackup --host $BIND --port $PORT --env $ENVIROMENT --server Starman --workers=$WORKERS --app /home/dancer/TestService/bin/app.psgi
WorkingDirectory=/home/dancer/TestService
ExecStop=/bin/kill -s QUIT $MAINPID
KillMode=mixed
KillSignal=QUIT
StandardOutput=journal
StandardError=journal
NoNewPrivileges=true
PrivateTmp=true
LimitNOFILE=infinity
Restart=on-failure
RestartSec=60s
[Install]
WantedBy=multi-user.target
start the service
systemd-analyze verify /etc/systemd/system/TestService.service
systemctl daemon-reload
systemctl cat TestService
systemctl enable TestService.service
systemctl start TestService
systemctl is-active TestService
systemctl status TestService
journalctl -f -xelu TestService
systemctl stop TestService
delete the service
systemctl stop TestService
systemctl disable TestService.service
unlink /etc/systemd/system/TestService.service
If you use an nginx web server to reverse proxy you service your app
vi nginx.conf
( run in 1.660 second using v1.01-cache-2.11-cpan-39bf76dae61 )