The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

MPMinus::REST - Base class of the MPMinus REST server

VERSION

Version 1.00

SYNOPSIS

    PerlModule MyApp::REST
    PerlOptions +GlobalRequest +ParseHeaders

    <Location />
        PerlInitHandler MyApp::REST
    </Location>
    <Location /foo>
        PerlInitHandler MyApp::REST
        PerlSetVar Location foo
        PerlSetVar Debug on
    </Location>

    package MyApp::REST;

    use base qw/MPMinus::REST/;

    sub handler : method {
        my $class = (@_ >= 2) ? shift : __PACKAGE__;
        my $r = shift;

        # ... preinit statements ...

        return $class->init($r);
    }

    sub hInit {
        my $self = shift;
        my $r = shift;

        # Dir config variables
        $self->set_dvar(testvalue => $r->dir_config("testvalue") // "");

        # Session variables
        $self->set_svar(init_label => __PACKAGE__);

        return $self->SUPER::hInit($r);
    }

    __PACKAGE__->register_handler( # GET /
        handler => "getIndex",
        method  => "GET",
        path    => "/",
        query   => undef,
        attrs   => {
                foo             => 'blah-blah-blah',
                #debug           => 'on', # off
                #content_type    => 'text/plain; charset=utf-8',
                #deserialize     => 1,
                #deserialize_att => {},
                serialize       => 1,
                #serialize_att   => {},
            },
        description => "Index",
        code    => sub {
        my $self = shift;
        my $name = shift;
        my $r = shift;
        my $q = $self->get("q");
        my $usr = $self->get("usr");
        my $req_data = $self->get("req_data");

        # Output
        my $uri = $r->uri || ""; $uri =~ s/\/+$//;
        $self->set( res_data => {
            foo_attr        => $self->get_attr("foo"),
            name            => $name,
            init_label      => $self->get_svar("init_label"),
            server_status   => $self->status,
            code            => $self->code,
            error           => $self->error,
            key             => $self->get_svar("key"),
            path            => $self->get_svar("path"),
            remote_addr     => $self->get_svar("remote_addr"),
            location        => $self->{location},
            #data            => $self->data,
            stamp           => $self->{stamp},
            dvars           => $self->{dvars},
            uri             => $uri,
            usr             => $usr,
            req_data        => $req_data,
            servers         => [$self->registered_servers],
        });

        return 1; # Or 0 only!!
    });

DESCRIPTION

This module allows you to quickly and easily write a mod_perl2 REST server

Please note! This module REQUIRES Apache 2.2+ and mod_perl 2.0+

So. And now about REST and RESTfull

Information bellow has been copied from page https://spring.io/understanding/REST

REST (Representational State Transfer) was introduced and defined in 2000 by Roy Fielding in his doctoral dissertation. REST is an architectural style for designing distributed systems. It is not a standard but a set of constraints, such as being stateless, having a client/server relationship, and a uniform interface. REST is not strictly related to HTTP, but it is most commonly associated with it.

REST AND CRUD

The MPMinus::REST tries to keep its HTTP methods joined up with CRUD methods.

The acronym CRUD refers to all of the major functions that are implemented in relational database applications. Each letter in the acronym can map to a standard Structured Query Language (SQL) statement, Hypertext Transfer Protocol (HTTP) method (this is typically used to build RESTful APIs) or Data Distribution Service (DDS) operation:

     CRUD             | SQL    | HTTP           | RESTful| DDS       | Prefix
    ------------------+--------+----------------+--------+-----------+--------
     Create           | INSERT | PUT/POST       | POST   | write     | add
     Read (Retrieve)  | SELECT | GET            | GET    | read/take | get/is
     Update (Modify)  | UPDATE | PUT/POST/PATCH | PUT    | write     | set
     Delete (Destroy) | DELETE | DELETE         | DELETE | dispose   | del/rm

Note! Create - PUT with a new URI; POST to a base URI returning a newly created URI

Note! Update - PUT with an existing URI

Note! HTTP PATCH - When PUTting a complete resource representation is cumbersome and utilizes more bandwidth, e.g.: when you have to update partially a column

PRINCIPIES OF REST

  • Resources expose easily understood directory structure URIs.

  • Representations transfer JSON or XML to represent data objects and attributes.

  • Messages use HTTP methods explicitly (for example, GET, POST, PUT, and DELETE).

  • Stateless interactions store no client context on the server between requests. State dependencies limit and restrict scalability. The client holds session state.

HTTP METHODS

Use HTTP methods to map CRUD (create, retrieve, update, delete) operations to HTTP requests.

GET

Retrieve information. GET requests must be safe and idempotent, meaning regardless of how many times it repeats with the same parameters, the results are the same. They can have side effects, but the user doesn't expect them, so they cannot be critical to the operation of the system. Requests can also be partial or conditional.

Retrieve an address with an ID of 1:

    GET /addresses/1

POST

Request that the resource at the URI do something with the provided entity. Often POST is used to create a new entity, but it can also be used to update an entity.

Create a new address:

    POST /addresses

PUT

Store an entity at a URI. PUT can create a new entity or update an existing one. A PUT request is idempotent. Idempotency is the main difference between the expectations of PUT versus a POST request.

Modify the address with an ID of 1:

    PUT /addresses/1

Note: PUT replaces an existing entity. If only a subset of data elements are provided, the rest will be replaced with empty or null.

PATCH

Update only the specified fields of an entity at a URI. A PATCH request is neither safe nor idempotent (RFC 5789). That's because a PATCH operation cannot ensure the entire resource has been updated.

    PATCH /addresses/1

DELETE

Request that a resource be removed; however, the resource does not have to be removed immediately. It could be an asynchronous or long-running request.

Delete an address with an ID of 1:

    DELETE /addresses/1

HTTP 1.1 STATUS CODES

1XX - informational
    100 HTTP_CONTINUE                        Continue
    101 HTTP_SWITCHING_PROTOCOLS             Switching Protocols
2XX - success
    200 HTTP_OK                              OK
    201 HTTP_CREATED                         Created
    202 HTTP_ACCEPTED                        Accepted
    203 HTTP_NON_AUTHORITATIVE               Non-Authoritative Information
    204 HTTP_NO_CONTENT                      No Content
    205 HTTP_RESET_CONTENT                   Reset Content
    206 HTTP_PARTIAL_CONTENT                 Partial Content
3XX - redirection
    300 HTTP_MULTIPLE_CHOICES                Multiple Choices
    301 HTTP_MOVED_PERMANENTLY               Moved Permanently
    302 HTTP_MOVED_TEMPORARILY               Found
    303 HTTP_SEE_OTHER                       See Other
    304 HTTP_NOT_MODIFIED                    Not Modified
    305 HTTP_USE_PROXY                       Use Proxy
    306                                      (Unused)
    307 HTTP_TEMPORARY_REDIRECT              Temporary Redirect
4XX - client error
    400 HTTP_BAD_REQUEST                     Bad Request
    401 HTTP_UNAUTHORIZED                    Unauthorized
    402 HTTP_PAYMENT_REQUIRED                Payment Required
    403 HTTP_FORBIDDEN                       Forbidden
    404 HTTP_NOT_FOUND                       Not Found
    405 HTTP_METHOD_NOT_ALLOWED              Method Not Allowed
    406 HTTP_NOT_ACCEPTABLE                  Not Acceptable
    407 HTTP_PROXY_AUTHENTICATION_REQUIRED   Proxy Authentication Required
    408 HTTP_REQUEST_TIMEOUT                 Request Timeout
    409 HTTP_CONFLICT                        Conflict
    410 HTTP_GONE                            Gone
    411 HTTP_LENGTH REQUIRED                 Length Required
    412 HTTP_PRECONDITION_FAILED             Precondition Failed
    413 HTTP_REQUEST_ENTITY_TOO_LARGE        Request Entity Too Large
    414 HTTP_REQUEST_URI_TOO_LARGE           Request-URI Too Long
    415 HTTP_UNSUPPORTED_MEDIA_TYPE          Unsupported Media Type
    416 HTTP_RANGE_NOT_SATISFIABLE           Requested Range Not Satisfiable
    417 HTTP_EXPECTATION_FAILED              Expectation Failed
5XX - server error
    500 HTTP_INTERNAL_SERVER_ERROR           Internal Server Error
    501 HTTP_NOT IMPLEMENTED                 Not Implemented
    502 HTTP_BAD_GATEWAY                     Bad Gateway
    503 HTTP_SERVICE_UNAVAILABLE             Service Unavailable
    504 HTTP_GATEWAY_TIME_OUT                Gateway Timeout
    505 HTTP_VERSION_NOT_SUPPORTED           HTTP Version Not Supported

See Apache Constants

MEDIA TYPES

The Accept and Content-Type HTTP headers can be used to describe the content being sent or requested within an HTTP request. The client may set Accept to application/json if it is requesting a response in JSON. Conversely, when sending data, setting the Content-Type to application/xml tells the client that the data being sent in the request is XML.

CONFIGURATION

The simplest configuration of MPMinus REST server requires a few lines in your httpd.conf:

    PerlModule MyApp::REST
    PerlOptions +GlobalRequest +ParseHeaders
    <Location />
        PerlInitHandler MyApp::REST
    </Location>

The <Location> section routes all requests to the MPMinus REST handler, which is a simple way to try out MPMinus REST

METHODS

handler

The main Apache mod_perl2 entry point. This method MUST BE overwritten in your class!

    sub handler : method {
        my $class = (@_ >= 2) ? shift : __PACKAGE__;
        my $r = shift;

        # ... preinit statements ...

        return $class->init($r, arg1 => "foo", arg2 => "bar");
    }

init

In your class of the server, you MUST SPECIFY the initializer call string that returns the mod_perl2 (Apache2::Const) code - common or http.

    my $rc = $class->init($r, ...ARGUMENTS...);

As first parameter ($r) is the Apache2::RequestRec object

Arguments is a hash-pairs (list of name and values) of additional parameters for RAMST base class

location, base

It is part of the URL-path that will bases for your REST methods, e.g., "/", "rest", "/foo"

prefix

Prefix name for signing LOG-strings and your files. Default: name of your server class

blank

Specifies blank-structure for RAMST working data in current request context. In the next request, all data will be reset according to the default data defined in the blank-structure.

Default blank is:

    {
        q           => undef, # CGI object
        usr         => {},    # User params (from QueryString or form-data)
        req_data    => '',    # Request data
        res_data    => '',    # Response data
    }

For get and set data please use get and set methods

dvars, valid_dvars

Defines defaults for variables specified in the httpd.conf <Location>, <Directory>, and <Files> section

Defaults:

    {
        debug       => 'off',
        location    => 'default',
    },
sr_attrs

Defines dafaults for CTK::Serializer

Defaults:

    {
        xml => [
                { # Serialize
                    RootName        => "response",
                    NoAttr          => 1,
                },
                { # Deserialize
                    ForceArray      => 1,
                    ForceContent    => 1,
                }
            ],
        json => [
                { # Sserialize
                    utf8            => 0,
                    pretty          => 1,
                    allow_nonref    => 1,
                    allow_blessed   => 1,
                },
                { # Deserialize
                    utf8            => 0,
                    pretty          => 1,
                    allow_nonref    => 1,
                    allow_blessed   => 1,
                },
            ],
    }

registered_servers

    my @servers = $self->registered_servers();

Returns list of the defined RAMST server's instances

error_response

    sub error_response {
        my $self = shift;
        return {
            error => {
                code    => $self->code,
                message => $self->error
            }
        }
    }

Defines error response format

You can override this method in your class

HANDLER METHODS

You can override all of these methods in your class

hInit

The First (Init) handler method

    sub hInit {
        my $self = shift;
        my $r = shift;

        # Dir config variables
        $self->set_dvar(testvalue => $r->dir_config("testvalue") // "");

        # Session variables
        $self->set_svar(init_label => __PACKAGE__);

        return $self->SUPER::hInit($r);
    }

By default the method performs:

  1. Sets the hitime and the sid session variables (svars)

  2. Checks the HTTP Method of the current request

  3. Inits CGI query object and sets it as the "q" node in RAMST data structure

  4. Inits usr structure from $q->param's (QUERY_STRING parsed params) and sets it as the "usr" node in RAMST data structure

  5. Inits request data from REQUEST in "as-is" format and sets it as the "req_data" node in RAMST data structure

  6. Sets "debug" session variable from dvars or RAMST handler's attributes

Type: RUN_ALL

See http://perl.apache.org/docs/2.0/user/handlers/http.html

hAccess

The Access handler method

    sub hAccess {
        my $self = shift;
        my $r = shift;
        # ... your statements ...
        return $self->SUPER::hAccess($r);
    }

By default the method sets remote_addr session variable from X-Real-IP header or remote_ip of Apache2::Connection method

Type: RUN_ALL

See http://perl.apache.org/docs/2.0/user/handlers/http.html

hAuthen

The Authen handler method

    sub hAuthen {
        my $self = shift;
        my $r = shift;
        # ... your statements ...
        return $self->SUPER::hAuthen($r);
    }

By default the method nothing to do and returns Apache2::Const::DECLINED response code

Type: RUN_FIRST

See http://perl.apache.org/docs/2.0/user/handlers/http.html

hAuthz

The Authz handler method

    sub hAuthz {
        my $self = shift;
        my $r = shift;
        # ... your statements ...
        return $self->SUPER::hAuthz($r);
    }

By default the method nothing to do and returns Apache2::Const::DECLINED response code

Type: RUN_FIRST

See http://perl.apache.org/docs/2.0/user/handlers/http.html

hType

The Type handler method

    sub hType {
        my $self = shift;
        my $r = shift;
        # ... your statements ...
        return $self->SUPER::hType($r);
    }

By default the method sets the "format" session variable and sets Content-Type header according defined format

Type: RUN_FIRST

See http://perl.apache.org/docs/2.0/user/handlers/http.html

hFixup

The Fixup handler method

    sub hFixup {
        my $self = shift;
        my $r = shift;
        # ... your statements ...
        return $self->SUPER::hFixup($r);
    }

By default the method sets the "serializer" session variable and sets serializer's attributes as "deserialize_attr" and "serialize_attr" session variables (svars). Also this method sets the "req_data" data node according selected serialization

Type: RUN_ALL

See http://perl.apache.org/docs/2.0/user/handlers/http.html

hResponse

The Response handler method

    sub hResponse {
        my $self = shift;
        my $r = shift;
        # ... your statements ...
        return $self->SUPER::hResponse($r);
    }

By default the method runs RAMST registered handler, gets data and prepare response content (or error response content), serialize it and sents to client

Type: RUN_FIRST

See http://perl.apache.org/docs/2.0/user/handlers/http.html

hLog

The Log handler method

    sub hLog {
        my $self = shift;
        my $r = shift;
        # ... your statements ...
        return $self->SUPER::hLog($r);
    }

By default the method writes to system log status of the current request-transaction

Type: RUN_ALL

See http://perl.apache.org/docs/2.0/user/handlers/http.html

hCleanup

The Cleanup handler method

    sub hCleanup {
        my $self = shift;
        my $r = shift;
        # ... your statements ...
        return $self->SUPER::hCleanup($r);
    }

By default the method flushes the RAMST data node to blank-data structure, flushes session variables (svars) and resets status and errors of current RAMST server object

Type: RUN_ALL

See http://perl.apache.org/docs/2.0/user/handlers/http.html

VARIABLES

MPMinus REST supports current session variables (svars), directory-variables (dvars) and data variables (data)

SESSION VARIABLES

Session variables are set only per session and are valid only within the current request

debug

Defines DEBUG flag. Contains boolean values 0 or 1

Default: 0

Defined in: hInit

format

Format name of the current transaction

Possible values: xml, yaml, json, none

Default: none

Defined in: hType

hitime

High time value (microseconds)

Defined in: hInit

key

Key of the current handler

Default: GET#/#default

Defined in: hInit

method

Current HTTP method

Default: GET

Defined in: hInit

name

Name of the current handler

Default: ""

Defined in: hResponse

path

Current URL-path

Default: /

Defined in: hInit

query

Current URL-query string

Default: ""

Defined in: hInit

remote_addr

Current remote client IP address

Default: 127.0.0.1

Defined in: hAccess

serializer

Serializer object

Default: undef

Defined in: hFixup

sid

Session ID

Defined in: hInit

deserialize_attr

Deserialize attributes

Default: undef

Defined in: hFixup

serialize_attr

Serialize attributes

Default: undef

Defined in: hFixup

DIRECTORY VARIABLES

Variables obtained from the Apache configuration sections <Location>, <Directory> or <Files> using PerlSetVar and PerlAddVar directives.

These variables do not flushes automatically in cleanup handler, so the dvars pool can be used for persistent objects, e.g., DBI

debug

Debug value

Possible values: on, yes, 1, off, no, 0

Default: off

location

Base location name or path

Default: default

DATA VARIABLES

These variables are initialized to BLANK structure and then modified from the handler to the handler. The Data variables are automatically flushes at the Cleanup handler.

By default BLANK is follow structure:

    {
        q           => undef, # CGI object
        usr         => {},    # User params (from QueryString or form-data)
        req_data    => '',    # Request data
        res_data    => '',    # Response data
    }
req_data

Request data

Sets in: hInit, hFixup

res_data

Response data

Sets in: hResponse (RAMST handler)

usr

User params (from QueryString or form-data)

Sets in: hInit

q

CGI object

Sets in: hInit

HISTORY

See CHANGES file

DEPENDENCIES

mod_perl2, MPMinus, MPMinus::RAMST, CTK::Serializer, CGI

TO DO

See TODO file

BUGS

* none noted

SEE ALSO

mod_perl2, https://www.restapitutorial.com, https://spring.io/understanding/REST, https://restfulapi.net

AUTHOR

Serż Minus (Sergey Lepenkov) http://www.serzik.com <abalama@cpan.org>

COPYRIGHT

Copyright (C) 1998-2019 D&D Corporation. All Rights Reserved

LICENSE

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See LICENSE file and https://dev.perl.org/licenses/