Sub::Spec::HTTP::Server - PSGI application to serve remote (HTTP) subroutine call requests
version 0.12
Suppose you want to expose functions in My::API::Adder and My::API::Adder::Array as HTTP API functions, using URL http://<host>/api/<module>/<func>:
My::API::Adder
My::API::Adder::Array
package My::API::Adder; our %SPEC; $SPEC{add} = {args => {a=>["float*"=>{arg_pos=>0}], b=>["float*"=>{arg_pos=>1}]}}; sub add { my %args=@_; [200, "OK", $args{a}+$args{b}] } 1; package My::API::Adder::Array; $SPEC{add_array} = { summary => 'Concatenate two arrays together', args => {a1=>["array*" => {summary => 'First array'}], a2=>["array*" => {summary => 'Second array'}]}, }; sub add { my %args=@_; [200, "OK", [@{$args{a1}}, @{$args{a2}}]] } 1; # or just write any normal Perl module without any specs
Then:
$ servepm My::API::Adder My::API::Adder::Array
Then call your functions over HTTP(S)?:
% curl http://localhost:5000/api/My::API::Adder/add/2/3 [200,"OK",6] % curl -H 'X-SS-Req-Log-Level: trace' \ 'http://localhost:5000/api/My::API::Adder::Array/add?a1:j=[1]&a2:j=[2,3]' [200,"OK",[1,2,3]]
Request help/usage information:
% curl -H 'X-SS-Req-Command: help' \ 'http://localhost:5000/api/My::API::Adder::Array/add' My::API::Adder::Array::add - Concatenate two arrays together Arguments: a1 (array, required) First array a2 (array, required) Second array
List available function in a module (request key 'command' given in request variable):
% curl 'http://localhost:5000/api/My::API::Adder?-ss-req-command=list_subs' ['add']
List available modules:
% curl -H 'X-SS-Req-Command: list_mods' \ 'http://localhost:5000/api/' ['My::API::Adder','My::API::Adder::Array']
NOTICE: This module and the Sub::Spec standard is deprecated as of Jan 2012. Rinci is the new specification to replace Sub::Spec, it is about 95% compatible with Sub::Spec, but corrects a few issues and is more generic. Perinci::* is the Perl implementation for Rinci and many of its modules can handle existing Sub::Spec sub specs. Serabi (Perinci::HTTP::Server) supersedes this module.
Perinci::*
Sub::Spec::HTTP::Server is a PSGI application to serve remote (HTTP) subroutine call requests. It is suitable for serving remote API. (Sorry for the slight confusion between "server" and "application"; this module was not originally PSGI-based.)
As the case with any PSGI application, you can use any PSGI server to run it with. But you might want to consider Gepok, which has built-in HTTPS support.
This PSGI application is actually a set of modular middlewares (Plack::Middleware::SubSpec::*) which you can compose in your app.psgi, configuring each one and including only the ones you need. The Synopsis shows one such basic composition. There are more middlewares to do custom stuffs. See each middleware's documentation for details.
This module uses Log::Any for logging.
This module uses Moo for object system.
For example, instead of:
http://localhost:5000/api/My::API::Adder/func
you want:
http://localhost:5000/adder/func
or perhaps (if you only have one module to expose):
http://localhost:5000/func
You can do this by customizing uri_pattern when enabling SubSpec::ParseRequest middleware (see servepm source code). You just need to make sure that you produce $env->{"ss.request"}{uri} (and other necessary SS request keys).
Again, this can be achieved by customizing the SubSpec::ParseRequest middleware. You can do something like:
enable "SubSpec::ParseRequest" uri_pattern => qr!^/api/v1/(?<output_format>json|yaml)/ (?<module>[^?/]+)? (?:/(?<sub>[^?/]+)?)!x;
or:
enable "SubSpec::ParseRequest" uri_pattern => qr!^/api/v1/(?<fmt>j|y)/ (?<module>[^?/]+)? (?:/(?<sub>[^?/]+)?)!x, after_parse => sub { my $env = shift; my $fmt = $env->{"ss.uri_pattern_matches"}{fmt}; $env->{"ss.request"}{output_format} = $fmt =~ /j/ ? 'json' : 'yaml'; };
You can leave uri_pattern empty and perform your custom URI parsing in after_parse. For example:
uri_pattern
after_parse
enable "SubSpec::ParseRequest" after_parse => sub { my $env = shift; # parse $env->{REQUEST_URI} on your own and put the result in # $env->{"ss.request"}{uri} };
Or alternatively you can write your own request parser to replace ParseRequest.
Supply --https_ports, --ssl_key_file and --ssl_cert_file options in servepm.
Well, isn't exposing functions the whole point of API?
If you have modules that you do not want to expose as API, simply exclude it (e.g. using allowed_modules configuration in SubSpec::ParseRequest middleware. Or, create a set of wrapper modules to expose only the functionalities that you want to expose.
allowed_modules
Or, if you have a custom mapping of "modules" and "subs" that are different than Perl's, I recommend you create a new Sub::Spec::URI::xxx implementation.
Add a format_<fmtname> method to Plack::Middleware::SubSpec::HandleCommand. The method accepts sub response and is expected to return a tuplet ($output, $content_type).
Note that you do not have to modify the Plack/Middleware/SubSpec/HandleCommand.pm file itself. You can inject the method from another file.
Also make sure that the output format is allowed (see configuration allowed_output_formats in the command handler middleware).
allowed_output_formats
Use one of the module-reloading module on CPAN, e.g.: Module::Reload or Module::Reload::Conditional.
Enable Plack::Middleware::Auth::Basic (or other authen middleware you prefer) before SubSpec::ParseRequest.
Take a look at Plack::Middleware::SubSpec::Authz::ACL which allows authorization based on various conditions. Normally this is put after authentication and before command handling.
Write Sub::Spec::HTTP::Server::Command::<cmdname>, and include the command in SubSpec::ParseRequest's allowed_commands configuration.
allowed_commands
But first consider if that is really what you want. If you want to serve static files or do stuffs unrelated to calling subroutines or subroutine spec, you ought to put it somewhere else, e.g.:
my $app = builder { mount "/api" => builder { enable "SubSpec::ParseRequest", ...; ... }, mount "/static" => ... };
Not only can you serve local modules ("pm://" URIs), you can serve remote modules ("http://" or "https://" URIs) making your API server a proxy for another.
To be written.
* Improve performance.
Sub::Spec::HTTP
Steven Haryanto <stevenharyanto@gmail.com>
This software is copyright (c) 2012 by Steven Haryanto.
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 Sub::Spec::HTTP::Server, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Sub::Spec::HTTP::Server
CPAN shell
perl -MCPAN -e shell install Sub::Spec::HTTP::Server
For more information on module installation, please visit the detailed CPAN module installation guide.