The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Dancer2::Plugin::WebService - Rapid creation of RESTful Web Services with sessions and persistent data

VERSION

version 3.100

SYNOPSIS

At your Application/service you can use all 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

Route examples

using the curl as client

curl 0:3000/info
curl 0:3000/info/client
curl 0:3000/info/version
curl 0:3000/info/version?to=json
curl 0:3000/info/version?to=yaml
curl 0:3000/info/version?to=xml
curl 0:3000/info/version?to=perl
curl 0:3000/info/version?to=human

curl 0:3000/route1
curl 0:3000/route2
curl -d '{"k1":"v1"}' 0:3000/route2
curl 0:3000/route3
curl 0:3000/error

curl -d '{"k1":"v1", "k2":"v2"}'  0:3000/get1
curl -d '{"k1":"v1" }'            0:3000/get2?to=human
curl -d '{"k1":"v1", "k2":"v2"}'  0:3000/get2?to=xml

curl -d '{"k1":"v1", "k2":"v2"}'  0:3000/mirror
curl -d '{"k1":"v1", "k2":"v2"}' '0:3000/mirror?to=xml'
curl -d '{"k1":"v1", "k2":"v2"}' '0:3000/mirror?to=yaml'
curl -d '{"k1":"v1", "k2":"v2"}' '0:3000/mirror?to=perl'
curl -d '<D><k1>v1</k1></D>'     '0:3000/mirror?from=xml;to=human'

curl -d '{"k1":"v1", "k2":"v2"}'               0:3000/secure
curl -d '{"user":"user2", "password":"pass2"}' 0:3000/login
curl -d '{"SessionID":"2d85b82b158e", "k1":"v1", "k2":"v2"}' 0:3000/secure
curl -d '{"SessionID":"2d85b82b158e"                      }' 0:3000/secure
curl -d '{"SessionID":"2d85b82b158e"}' 0:3000/logout

Service example

implementing the above routes

package TestService;
use     Dancer2;
use     Dancer2::Plugin::WebService;
use     strict;
use     warnings;

any '/mirror' => sub { reply 'SEND_DATA' };
get '/route1' => sub { reply };
any '/route2' => sub { reply 'k1' };
get '/route3' => sub { reply 'k1'=>'v1', 'k2'=>'v2' };

get '/error'  => sub { reply 'k1', 'v1', 'error', '37', 'errormessage', 'fever' };

any '/get1'   => sub { my  %all = get_data_post;
                 reply %all };

any '/get2'   => sub { my ($v1, $v2) = get_data_post('k1', 'k2');
                 reply 'foo'=>$v1 , 'boo'=>$v2
                 };

any '/secure' => sub {
                 my %send = get_data_post;
                            set_data_session('s1'=>'L1', 's2'=>'L2', 's3'=>['A', 'B']);
                            del_data_session('s2', 's8', 's9');
                 my %All  = get_data_session();
                 my @Some = get_data_session('s1', 's7');

                 reply( send=>{%send}, ses_all=>{%All}, s1=>$Some[0], s7=>$Some[1] )
                 };
dance;

NAME

Dancer2::Plugin::WebService - Rapid creation of RESTful Web Services with login/logout, sessions and persistent data

VERSION

version 3.100

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 0:3000/SomeRoute?to=json
curl 0:3000/SomeRoute?to=xml
curl 0:3000/SomeRoute?to=yaml
curl 0:3000/SomeRoute?to=perl
curl 0:3000/SomeRoute?to=human

curl -d '{"k1":"3", "k2":"30"}'               0:3000/SomeRoute
curl -d '{"k1":"3", "k2":"30"}'              '0:3000/SomeRoute?to=xml'
curl -d '{"k1":"3", "k2":"30"}'              '0:3000/SomeRoute?to=yaml'
curl -d '{"k1":"3", "k2":"30"}'              '0:3000/SomeRoute?to=perl'
curl -d '{"k1":"3", "k2":"30"}'              '0:3000/SomeRoute?from=json;to=human'
curl -d '<Data><k1>3</k1><k2>30</k2></Data>' '0:3000/SomeRoute?from=xml'
curl -d '<Data><k1>3</k1><k2>30</k2></Data>' '0:3000/SomeRoute?from=xml;to=human'
curl -d '<Data><k1>3</k1><k2>30</k2></Data>' '0:3000/SomeRoute?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, but you can post data and access them using the get_data_post

private are the routes that they need user to login . At private routes you can read, write, delete, update persistent data using the methods get_data_session , set_data_session , del_data_session

Persistent session data are auto deleted when you logout or if your session expired.

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 0:3000/info/version
curl 0:3000/info/version?to=yaml
curl 0:3000/info/version?to=xml
curl 0:3000/info/version?to=perl
curl 0:3000/info/version?to=human

info/client

Client information (public route)

curl 0:3000/info/client

info

Redirects to /info/version

login

Login for using private routes and storing persistent data. It is a public route.

curl --data '{"user":"dancer","password":"password"}' 0:3000/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":"0a1ad34505076d930c3f"}'  0:3000/logout
curl -X GET --data '{"SessionID":"0a8e4f0523dafa980ec3"}' '0:3000/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 : /var/lib/WebService

METHODS

reply

send the reply to the client, performing any necessary format convertions This should be the last route's statement

reply                           only the error and errormessage
reply    k1 => 'v1', ...        anything you want
reply( { k1 => 'v1', ... } )    anything you want
reply('SEND_DATA')              data send by the user
reply('k1')                     data send by the user only one key

get_data_post

Retrieves data user send to WebService with his client e.g curl. Use this to do something usefull with user's data.

my ($var1, $var2) = get_data_post('k1', 'k2');      return the selected keys
my %hash          = get_data_post();                return all data as hash

get_data_session

Retrieves session data. You have to be logged in.

my %data = get_data_session( 'k1', 'k2', ... );     return only the selected keys
my %data = get_data_session();                      returs all data as hash

set_data_session

Store persistent session data. Session data are not volatile like the user data between service calls. You have to be logged in

set_data_session(   new1 => 'foo1', new2 => 'foo2'   );
set_data_session( { new1 => 'foo1', new2 => 'foo2' } );

del_data_session

Deletes session data. You have to be logged in.

del_data_session( 'k1', 'k2', ... );                 deletes only the selected keys
del_data_session();                                  deletes all keys

INSTALLATION

After install Dancer2::Plugin::WebService you should run it as a non privileged user e.g. dancer . Be careful, non root users can not bind ports up to 1024

getent group  dancer >/dev/null || groupadd dancer
getent passwd dancer >/dev/null || useradd -g dancer -l -M -c "Dancer2 WebService" -s $(which nologin) dancer

mkdir /var/lib/WebService ; chown -R dancer:dancer /var/lib/WebService
mkdir /var/log/WebService ; chown -R dancer:dancer /var/log/WebService

If you have a firewall running you should create a rule for the listening port e.g. at Redhat

firewall-cmd --zone=public --permanent --add-port=3000/tcp
firewall-cmd --reload
firewall-cmd --list-all

create your application e.g TestService inside e.g. the /opt folder

dancer2 gen --application TestService --directory TestService --path /opt --overwrite
chown -R dancer:dancer /opt/TestService

If you want compressed replies edit the file /opt/TestService/bin/app.psgi

#!/usr/bin/env perl
use FindBin;
use lib "$FindBin::Bin/../lib";
use TestService;
use Plack::Builder;
builder { enable 'Deflater'; TestService->to_app }

Edit the file .../environments/production.yml

# logger    : file, console
# log level : core, debug, info, warning, error

startup_info     : 1
show_errors      : 1
warnings         : 1
no_server_tokens : 0

logger           : 'file'
log              : 'core'
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"}'

Edit the file as needed to match your code

Notice that the firse Active authorization method will be used, also the Allowed hosts are matched from top to bottom

/opt/TestService/config.yml

environment             : production
plugins                 :
  WebService            :
    Session directory   : /var/lib/WebService
    Session idle timout : 3600
    Default format      : json
    Routes              :
      secure            : private
      mirror            : public
      route1            : public
      route2            : public
      route3            : public
      error             : public
      get1              : public
      get2              : public

    Allowed hosts:

      - 127.*
      - 10.*
      - 172.??.?.*
      - 192.168.1.23
      - 4.?.?.??
      - ????:????:????:6d00:20c:29ff:*:ffa3
      - "*"

    User must belong to one or more of the groups:

      - power
      - storage
      - network

  Authentication methods:

    - Name     : simple
      Active   : yes
      Command  : INTERNAL
      Users    :
        <any>  : secret4all
        user1  : <any>		
        user2  : pass2

    - Name     : Linux native users
      Active   : yes
      Command  : MODULE_INSTALL_DIR/scripts/LinuxOS/AuthUser.pl
      Use sudo : yes

    - Name     : Basic Apache auth for simple users
      Active   : no
      Command  : MODULE_INSTALL_DIR/scripts/HttpBasic/users.pl
      Use sudo : no

    - Name     : Basic Apache auth for admins
      Active   : no
      Command  : MODULE_INSTALL_DIR/scripts/HttpBasic/admins.pl
      Use sudo : no

Write your code e.g at the file /opt/TestService/lib/TestService.pm

Check the example at the the start of this page

Start the service as user dancer listening at port e.g 3000 and 10 threads

sudo -u dancer /usr/local/bin/plackup --host 0.0.0.0 --port 3000 --server Starman --workers=10 --env production -a /opt/TestService/bin/app.psgi

or during development

sudo -u dancer /usr/local/bin/plackup --host 0.0.0.0 --port 3000 --server Starman --workers=1 --env production --Reload /opt/Dancer2-Plugin-WebService/lib/Dancer2/Plugin,/opt/TestService/lib,/opt/TestService/config.yml,/opt/TestService/environments -a /opt/TestService/bin/app.psgi
sudo -u dancer /usr/local/bin/plackup --host 0.0.0.0 --port 3000 -a /opt/TestService/bin/app.psgi

or without Plack

sudo -u dancer  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 internal or external authentication methods. The external are using custom scripts/programs. The available authentication methods are defined at your config.xml Only the first Active Authentication method will be used. The external scripts must be executable from the user running the service.

It is very easy to write a custom script and have any authentication you want. For writing your own scripts please read the AUTHENTICATION_SCRIPTS and review the existing scripts

If your authentication method needs sudo, you must add the user running the WebService ( dancer ) on a sudoers file e.g.

vi /etc/sudoers.d/WebService

  dancer ALL=NOPASSWD: /usr/local/share/perl5/Dancer2/Plugin/scripts/LinuxOS/AuthUser.pl

Lets view as an example the native Linux mechanism. We have

User must belong to one or more of the groups:

  - powerusers
  - postfix
  - tape

Authentication methods:

  - Name     : Linux native users
    Active   : yes
    Command  : MODULE_INSTALL_DIR/scripts/LinuxOS/AuthUser.pl
    Use sudo : yes

  ...

That means that if a user do not belong to any of the groups powerusers, postfix, tape the login will fail. Also because this work only with root priviliges we have Use sudo : yes

There are also built in authentication methods that do not need external scripts.

For example the simple method define the users and their passwords directly the config.yml file. It can be configured like

- Name     : simple
  Active   : yes
  Command  : INTERNAL
  Users    :
    <any>  : secret4all
    user1  : <any>		
    user2  : pass2

The <any> means ... any ! So if you want to allow logins no matter the username or the password you could have, write

<any>  : <any>

This make sense if you want to give anyone the ability for persistent data

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) 2018 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.com>

AUTHOR

George Bouras <george.mpouras@yandex.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2018 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.