Web::MREST(3pm)
okay bestie, sit down, grab a snack π, and get comfy, because you just opened Web::MREST(3pm) and we are doing this together. It's a manual page, but make it a vibe check. π
π NAME π
Web::MREST β Minimalistic REST server. That's the whole name. Periodt.
π VERSION π
Version 0.290. That's the one, no cap.
Development status
Real talk: Web::MREST is currently in "Alpha - feature freeze". There are almost certainly bugs lurking in the code, but all the features have been implemented. So the vibes are immaculate and there might be a little guy hiding in there. π iykyk.
β¨ SYNOPSIS β¨
Wanna take this module for a spin? Just run this, bestie:
$ mrest-standalone
Leave it running (do not close that console, I'm serious π«Ά), and from another console start up the command-line client:
$ mrest-cli
Then in the CLI client, type something like:
Web::MREST::CLI::Parser> get /
A 'GET' request gets sent for the root resource, and the CLI client will show you a representation of the response. Slay.
You can pull off basically the same thing with curl:
curl -v http://localhost:5000/ -X GET -H "Content-Type: application/json"
For more deets on using the CLI client, see Web::MREST::CLI. π
π¦ DESCRIPTION π¦
MREST stands for "minimalistic" or "mechanical" REST server. (Mechanical, because it relies on Web::Machine β iconic behavior honestly.)
Web::MREST gives you a fully functional REST server that you can start with one simple command. Out of the box, with zero modification, the server hands you a set of generalized resources that are perfect for demonstrating how the REST server works, or for testing. Free demo content, we love that πΈ
Developers can use Web::MREST as a platform for implementing their own REST servers, as described below. App::Dochazka::REST is a "real-world" example of such a server β main character energy, honestly π
If you want an introduction to REST and Web Services, go read Web::MREST::WebServicesIntro.
π₯ RFC2616 AS A STATE MACHINE π₯
RFC2616 is, of course, the HTTP 1.1 standard β not a state machine. But the authors of "Web Machine" (which was originally implemented in Erlang) had a neat little idea: represent it as a state machine and use that to implement a server for providing web services. Genius behavior, no notes. β¨
Web::Machine is, of course, the Perl port of Web Machine.
Web::MREST relies on Web::Machine to implement RFC2616. You can think of Web::MREST as an extra abstraction layer sitting on top of Web::Machine.
By itself, Web::Machine is not a server. It does not listen on a port, for example. Instead, it's designed to work (via Plack) with a PSGI-compliant web server.
The web server hands incoming requests over to Web::Machine, which runs the requests through its state machine. (The Web::Machine authors call the state machine "the FSM.") The best way to actually grasp the state machine is to picture it as a flow-chart. At every "decision node" of the flow-chart β where the flow can go one of two directions β Web::Machine calls the method corresponding to that node. Each node gets a letter and a number: like F7, O18, etc. Cute little naming scheme, obsessed.
The flow-chart implemented by the FSM can be found here <http://...> β and you are encouraged to have that open for reference while you read this documentation and implement your REST server. Like, genuinely, have it open. Two tabs, bestie. π¦
π SERVER STARTUP AND INHERITANCE SCHEME π
Standalone mode
Like I said up top, Web::MREST is capable of operating independently. To try it out, start the server like this:
$ mrest-standalone
And then point your browser at:
http://localhost:5000
If you peek inside the mrest-standlone script, you'll see it's just a wrapper for the mrest script, which takes two mandatory options. The first, --distro, is the name of the distribution in whose sharedir it should look for configuration files. The second, --module, is the name of the application's resource module β i.e. the ultimate module in the chain of inheritance.
In standalone mode, the actual command that gets run is:
mrest --distro=Web::MREST --module=Web::MREST::Dispatch
...which causes the chain of inheritance to be built up like this:
bin/mrest : calls Web::Machine->new; the Web::Machine object is blessed into Web::MREST::Dispatch
Web::MREST::Dispatch : inherits from Web::MREST::Entity
Web::MREST::Entity : inherits from Web::MREST::Resource
Web::MREST::Resource : inherits from Web::Machine::Resource
When you browse to http://0:5000 in standalone mode, you get a list of the sample REST resources that are available. For more info on those, see config/dispatch_Config.pm. π
With your application
Starting the server with your application is the exact same thing as described in "Standalone mode" above, except you swap out Web-MREST for the name of your distribution, and Web::MREST::Dispatch for the name of your ultimate resource module.
$ mrest YourApp-MREST YourApp::MREST::Dispatch
So like, in this example we're starting the server with the distribution YourApp-MREST, which is presumed to implement a chain of inheritance similar to Web::MREST's, i.e.:
Web::MREST -> YourApp::MREST::Resource -> YourApp::MREST::Dispatch
Thanks to this arrangement, the application developer can customize Web::MREST β and I mean fully customize it: not only providing her own resources and handlers, but even altering how the state machine operates, if necessary β by providing her own chain of inheritance and overriding various methods within it. It's giving power. π
Recapitulation
Since the above is quite important, let's go over it again, bestie:
The Web::MREST documentation will always refer to your application either as the "application" or as YourApp. The application should take the form of a Perl distribution, which should have:
- a distribution sharedir
- a resource module,
YourApp::Resource. - a dispatch module,
YourApp::Dispatch
For now, just think of these three components as "black boxes". We will cover their contents later. Promise. π€
The server (i.e. your application), is started by executing the mrest executable with the name of your application's distribution and the name of its dispatch module, which should be the ultimate module in the chain of inheritance.
$ mrest --distro YourApp --module YourApp::Dispatch
Under the hood the startup script β which you can review at bin/mrest β does essentially this:
use Web::Machine;
Web::Machine->new(
resource => 'YourApp::Dispatch',
)->to_app;
There are two key points concerning the Web::Machine object constructed by that call to Web::Machine->new:
- the object is blessed into
YourApp::Dispatch - the object is a Plack application
Two points, two slays. π
π INHERITANCE SCHEME π
Like we saw in the previous section, YourApp inherits from Web::MREST through a chain of inheritance. Here's the chain that Web::MREST itself implements:
-> Web::MREST::Dispatch
-> Web::MREST::Entity
-> Web::MREST::Resource
-> Web::Machine::Resource
-> Plack::Component
Now, assuming YourApp has its authentication and authorization routines in YourApp::Resource and its resource definitions and handlers in YourApp::Dispatch, the chain for YourApp would look like this:
-> YourApp::Dispatch
-> YourApp::Resource
-> Web::MREST::Entity
-> Web::MREST::Resource
-> Web::Machine::Resource
-> Plack::Component
(In other words: YourApp::Dispatch and YourApp::Resource replace Web::MREST::Dispatch, which is just a demo. A little demo guy. π)
When Web::Machine reaches a given node in the FSM, it calls the corresponding method on that Web::Machine object. And since the object is blessed into YourApp::Dispatch, that module is where Perl will start looking for the method.
If the method isn't found at the lowest level, Perl follows the chain of inheritance "upward". The highest level, Plack::Component, is shown only for completeness β Web::MREST::Resource and Web::MREST::Entity implement all the methods that your resource module might (or should) want to override.
Readers who are not well-versed in writing Perl applications that use inheritance are referred to the fine Perl manuals such as perlootut. Go read it, bestie, it's good for you. π
π« STATE MACHINE INTRODUCTION π«
At this point we have enough background info to start grasping the state machine. (Instead of writing "state machine" we're gonna follow the Web::Machine convention and call it the "FSM".) This section presents selected features and nodes of the FSM, how Web::MREST implements them, and how to use them. The discourse proceeds in the order in which the methods get called when an HTTP request enters the FSM. We can picture these method calls as decision nodes of a flow-chart, or "cogs" of the FSM. βοΈ
And we don't have to just imagine the flow-chart β it actually exists and can be downloaded from ..... If you wanna understand how Web::Machine and Web::MREST work, this document is of fundamental importance. From here on out it will be referred to as "the FSM diagram".
As you can see in the FSM diagram, each FSM cog has a code like B6, for ease of reference.
okay and that's where we're pausing, because there is so much more coming and I refuse to cram it all into one breath π next part incoming...
π POLICIES AND FEATURES π
ok so here's the tea, bestie: Web::Machine implements the FSM, and that is literally all it does. It imposes zero policies on the distributions that use it. And that's on purpose!! By keeping things that neutral, Web::Machine maximizes its range of potential uses. iconic behaviour ngl.
But here's the thing β as powerful as it is, Web::Machine can be confusing to use. When the author started writing their first app on top of it, they found themselves wanting an intermediate module sitting between the app and Web::Machine β something that makes Web::Machine a lil more friendly π«Ά.
That module is Web::MREST. It builds on Web::Machine to provide certain additional features. And yeah, inevitably that means imposing some policies (i.e. limitations) on users. Acceptable trade-off, fr fr. π
πΈ Path dispatch πΈ
A key part of any web app is "path dispatch" (i.e. URI translation), which answers the big question: "how are URIs mapped to resources?"
So Web::Machine gives you a way to specify handlers for various media types that may appear in request and response entities β cute β but it provides no way of getting from the URI to the handler. Web::MREST bridges that gap with a system of resource definitions (see "Resource definitions", below).
Each resource definition spells out the URI-to-resource mapping and gives you the name of the resource's handler method. Internally, Web::MREST uses a single Path::Router object to parse URIs.
Before any URIs can be parsed, that Path::Router object has to be initialized. This happens in Web::MREST::Resource, in the service_available method. That method checks the scalar variable that's supposed to hold the Path::Router object and, if needed, calls init_router to initialize it. π
In the Web::MREST demo application, init_router is implemented in Web::MREST::Dispatch.
β¨ Resource handlers β¨
The Web::Machine docs mention "handlers" but do not go into any detail on how to actually write them. Rude, honestly. Web::MREST not only ships some working resource handlers, it also implements a whole paradigm for writing them. π
In this paradigm, the handler is called as a method, just like any of the other methods in the chain of inheritance. (To dodge namespace issues, it's recommended that handler method names start with handler_.) The name of the method is specified in the resource definition.
The handler method gets called twice β in other words, there are two passes:
- In the first pass, the handler is called with the argument
1(scalar value) and is expected to return a boolean value indicating whether the resource exists. - In the second pass, indicated by the argument
2(scalar value), the handler is expected to return anApp::CELL::Statusobject. This object (rendered in JSON) becomes the response entity unless a declared status takes over (see "mrest_declare_status" in Web::MREST::Resource).
N.B.: The request entity is not available to the handler (via $self-"context->{request_entity}>) until the second pass!!
π Status objects π
Like we said up there, App::CELL::Status objects are what resource handlers return. And not just that β Web::MREST tries its absolute best to always return an App::CELL::Status object in the response entity. main character energy π
Actually, it's not the object itself that gets returned, it's a JSON representation of its underlying data structure. And from that, the object can super easily be reconstituted on the client side by doing:
my $status = $JSON->decode( $response_entity );
bless $status, 'App::CELL::Status';
For more on what status objects can do, go look at App::CELL::Status, App::CELL, and App::CELL::Guide. π
π¦ Error statuses π¦
Web::Machine always tries to return the proper HTTP status code in the response. But the app developer will likely need to "force" a code in certain cases. Like, for example, the request may be "malformed" in a way that isn't discoverable until the handler runs. Or caught exceptions may need to be exposed to the client with 500 - Internal Error.
Also, the RFC says:
. . . the server SHOULD include an entity containing an explanation of the
error situation, and whether it is a temporary or permanent condition.
So clearly a mechanism is needed for providing such explanations and indicating whether the error is temporary or permanent. And that mechanism should enable an arbitrary status code to be declared. periodt.
By itself, Web::Machine doesn't really provide such a mechanism. What it does provide is a mechanism for "forcing" an arbitrary status code (e.g. 404 - Not Found) by returning a scalar reference. And that mechanism has two disadvantages:
- it is only available at certain junctions of the FSM
- I wanted a way to "declare" a status code at any point and be certain that Web::Machine won't change it later on.
- there is no obvious way to provide an explanation of the error
- Web::Machine considers this an implementation detail.
Hence, Web::MREST provides the mrest_declare_status method. To learn how to call it and how it works, see Web::MREST::Resource. π
π THE FINE STATE MACHINE π
In this section we take a detailed look at the FSM by considering some common scenarios. For our purposes these are "GET", "POST", "PUT", and "DELETE" requests. Handling can differ according to whether or not a "POST" creates a new resource and whether or not the resource is determined to exist.
β Part One (sanity checks and information gathering) β
The first few cogs are executed, in the same order, on all requests regardless of method. Think of them both as a set of sanity checks and as an information-gathering process. π§
πΈ service_available (B13) πΈ
The first method call is service_available, which is implemented by Web::MREST::Resource and should not be implemented by your application, because it calls init_router to make sure all the resource definitions are loaded and the Path::Router singleton is properly initialized.
That's not really a limitation though! Whatever code you need to run here can go in a method called mrest_service_available, which should return a boolean value (i.e. 1 or 0), and that determines the return value from the method.
If the service really isn't available, you can return false, which triggers a 503 Service Not Available response. Before you return, you should do:
$self->mrest_declare_status( explanation => '...', permanent => 0 );
to give an explanation of what's going on. π«Ά
For details, see the t/503-Service-Unavailable.t unit test.
β¨ known_methods (B12) β¨
Returns the list of supported ("known") methods in $site->MREST_SUPPORTED_HTTP_METHODS. If the request method isn't in that list, a 501 Not Implemented response is returned, along with an explanation that the method requested is not supported.
If that behaviour isn't it for you, the method can be implemented by the application instead. π
π uri_too_long (B11) π
If the request URI is longer than the value set in the MREST_MAX_LENGTH_URI site parameter, the client gets a 414 Request URI Too Long response.
To override this behaviour, just provide your own uri_too_long routine in your resource module. slay.
This functionality is demonstrated by the t/414-Request-URI-Too-Long.t unit.
π¦ allowed_methods (B10) π¦
"Is the method allowed on this resource?"
ok so this next routine is where things start getting complicated, bestie. According to the Web::Machine::Resource documentation β https://metacpan.org/pod/Web::Machine::Resource#allowed_methods β we're expected to respond with a list of methods allowed on the resource. And to assemble a list like that, we have to answer two questions first:
- Have the resource definitions been loaded?
- Does the URI match a known resource?
After the server starts, the first time this method is called it triggers a call to the init_router method, which populates the $resources package variable in Web::MREST::InitRouter with all the resource definitions. This is explained in detail in "Resource definitions". So that takes care of question number one. β
Question number two gets answered by Path::Router. And once the request has been associated with a known resource, finishing our task is just a matter of getting and returning the set of methods for which the resource is defined.
π malformed_request (B9) π
A true return value from this method triggers a 400 Bad Request response status. RFC2616 does not stipulate exactly what constitutes a bad request. We already (back in allowed_methods) handled the case where the URI fails to match a known resource, and that includes applying any validations properties from the resource definition.
So, in this method (or your overlay) we take the "next step" (whatever that is) in vetting the request. Keep in mind that this method is called before the resource handler. If you have any sanity checks you want to apply after the URI is matched to a resource but before the resource handler fires, this is the place to put them. π
If you'd like to keep Web::MREST's implementation of this method (which, for example, pushes the Content-Length and Content-Type information onto the context) and add your own logic, you can put it in mrest_malformed_request instead of overriding malformed_request itself. cute, right?
If you intend to return false from this method you should first do this:
$self->mrest_declare_status( explanation => '...' );
to make sure an explanation is included with the 400 response.
π is_authorized (B8) π
In the author's mind, "authentication" is the process of determining who the user is, and "authorization" determines if the user is allowed to do what she is asking to do. But RFC2616 does not make such a clear distinction. π
For that reason, it's left to the application to implement this method if needed.
π forbidden (B7) π
The same thoughts as expressed under "is_authorized", above, apply to this method as well.
π valid_content_headers (B6) π
This is where you vet the "Content-*" headers in the request. If the request contains any invalid "Content-*" headers (i.e., if the '*' part does not appear in << $site->MREST_VALID_CONTENT_HEADERS >>), a 501 will be generated.
The content headers are passed to the method in a Hash::MultiValue object. π§Ί
β known_content_type (B5) β
If the "Content-Type" header is relevant β i.e., if this is a PUT or POST request and if there is a request entity β check it against << $site->MREST_SUPPORTED_CONTENT_TYPES >>.
πΈ valid_entity_length (B4) πΈ
A simple little routine that compares the entity length (in bytes) with the maximum set in $site->MREST_MAX_LENGTH_REQUEST_BODY.
π options (B3) π
If your application needs to support the "OPTIONS" method, you should implement this yourself β otherwise, just ignore it. π
π Part Two (content negotiation)
Okay bestie, so the HTTP standard is a lot. Like, it has this whole complicated logic so clients and servers can "negotiate" the format (media type), the language, the encoding, everything about how content gets passed back and forth. π And in the Web::MREST docs we areβ¦ glossing over that. We focus on the media type only. But heads up: Web::Machine does include methods for all the content negotiation decision nodes, so if you're an application developer you're totally free to go use them.
That said β Web::MREST itself ships JSON handlers for both the request entity and the response entity, and it should be fully UTF-8 clean. So hopefully that saves you some work. π«Ά (For the whole story, go read "STATUS OBJECTS AND ERROR HANDLING".)
The next few subsections break down the main content negotiation methods. π
content_types_provided
The Web::Machine::Resource docs say this method must be implemented (by the application, that's you) β otherwise, and I quote, "your resource will not be able to return any useful content". π
And quoting some more: "This should return an ARRAY of HASH ref pairs where the key is the name of the media type and the value is a CODE ref (or name of a method) which can provide a resource representation in that media type."
The implementation Web::MREST gives you lets clients pick (through the Accept header) one of two media types:
text/htmlβ since it's the first hashref pair of the two, it's the default. Meaning: if the incoming request has noAcceptheader at all, the handler you specified fortext/htmlgets called to generate the response entity.application/jsonβ this is the media type Web::MREST was literally written to support, both in request entities and response entities. That said, nothing is stopping you, the app developer, from specifying handlers for any other media types you want. β¨
If the request does have an Accept header, but none of the media types in it show up in content_types_provided, Web::Machine will generate a 406 Not Acceptable response. (And unfortunately β no easy way around this β there's no way for Web::MREST or the application to know in advance that this error is about to fire, so it comes back "bare", i.e. with no explanatory response entity. It's giving minimalism whether you want it or not. π)
In the normal case, when an acceptable handler does exist, it gets called to generate the response β so basically whatever that chosen handler returns becomes the response entity, unless an error happens inside the handler. In that case the handler should return a reference to a scalar value (like \400), and Web::Machine reads that as an HTTP response code. See "STATUS OBJECTS AND ERROR HANDLING".
For more about response entity generation, check the sections all about the individual HTTP methods (GET, PUT, POST, DELETE), down below. π
content_types_accepted
When a client sends PUT or POST requests, it'll typically bring a Content-Type header saying what media type the bytes in the request body are. That content type gets compared against the media types this method returns. No match? Web::Machine hands back a 415 Unsupported Media Type error response. (And again β unfortunately there's no easy way for Web::MREST or the application to know in advance this error is coming, so it's returned "bare", no explanatory response entity. π)
Other methods
For handling character sets, encodings and languages, Web::Machine provides a bunch of other content negotiation methods:
charsets_provideddefault_charsetlanguages_providedencodings_providedvariances
But lowkey, you only need these if your application does complex content negotiation. If you don't, don't stress it. π§
πΈ Part Three (resource existence)
So once we've made it past content negotiation, we know way more than just which routines are going to process the request entity (if there is one) and generate the response. We've gathered quite a bit of info about the request, and all of it has been pushed onto the context, so every single one of our resource methods can see it β including the resource handler we're about to get to. π Here's what's on there:
(FIXME: verify this list as it is outdated)
methodβ the request method.resource_nameβ the resource name, which you can use as a key to look up the full resource definition in the$Web::MREST::InitRouter::resources.handler_nameβ the name of the resource handler, e.g.handler_bugreport. In Web::MREST, resource handlers live in the Web::MREST::Dispatch module.uriβ the full URI that came with the request.uri_baseβ the base part of the URI (e.g.http://localhost:5000/).uri_pathβ the relative path to the resource (e.g./bugreport).componentsβ a reference to an array whose elements are the individual 'components' (i.e. everything sitting between the/characters) of theuri_path.mappingβ a hashref mapping resource parameter names (if there are any) to their values.content-lengthβ the content-length header.content-typeβ the content-type header.
But here's the thing bestieβ¦ one major piece of info is missing: whether the resource exists or not. π³ And for that, we actually have to call the resource handler.
resource_exists (G7)
The term "resource" is not precisely defined, and honestly that's just how it is. It can mean the resource definition (a data structure), the resource handler (a Perl subroutine called as an object method), or an object (a set of records) in some underlying database. Or all of the above. Or something else entirely. The next few paragraphs explain how Web::MREST rolls with it. π
By the time control reaches this method, the request URI has already been matched to a resource definition, so the resource handler is known. And since we have literally no other way of knowing, we ask the resource itself β by calling the handler with the scalar value 1 (yes, the numeral 1) as its sole argument. β¨ This handler call is called the "first pass".
How you implement the handler is not our business. We only ask that it returns a boolean value (true or false) when called with this argument. If the return value from the handler is true, we can assume the handler will get called again (second pass) during the response generation phase β keep reading. π
π Part Four (generation of response entity)
So at this point we have:
- gathered information about the request and placed it on the context
- run the resource handler (first pass) to determine resource existence
And up until now β like, all the way through determining resource existence β the FSM has been a series of steps applied in the same order, no matter what the HTTP method is. Iconic of it, honestly.
But in the sections below we get into how responses are generated for each of the four HTTP methods (GET, PUT, POST, and DELETE) when the resource exists, and when it doesn't. π¦
Resource exists
GET
content_types_providedmethod call β first,content_types_providedis called so we can figure out the name of the method that can generate the response in the format that's required. When we say "response generator", that method is what we mean.- Response generator method call β second, the response generator gets called (from
o18in Web::Machine::FSM::States). It's expected to always return an App::CELL::Status object. If it detects an error condition, the handler should declare it using$self->mrest_declare_statusand then return anon_okstatus.
Also, important: GET is the only request method that demands a response entity in the format specified by the Accept header. For the other methods, response entities are optional β but recommended. In practice that means we have to create them ourselves. π
POST
Here we have two possible paths, and which one we take depends on what post_is_create returns:
post_is_createtruecreate_pathandcreate_path_after_handlerβ if, and only if,post_is_createis true, processing continues throughcreate_pathandcreate_path_after_handler. Depending on the value of the latter, the request handler (which we determine by consultingcontent_types_accepted) is called either before or aftercreate_path.- The request handler should stage the response entity, in preparation for finalization. The content type can be inferred from
$request->env->{'web.machine.context'}. - Finalization β the request is finalized by a call to
finish_request.
post_is_createfalse- If
post_is_createreturns falseβ¦ all bets are off, bestie. π For reasons I do not understand, Web::Machine does not consultcontent_types_providedorcontent_types_acceptedon this type of request. The only thing it does is callprocess_post, and so it's up to this method to do whatever needs doing to generate an entity and get it into the response. - Web::MREST helps out here by making sure the content type is stored in the context (in the 'content_type' property), so
process_postcan look there for it and generate the response entity accordingly. π«Ά
- If
PUT
On all PUT requests, and those POST requests that are handled as PUT requests (see above), Web::Machine uses this process:
content_types_acceptedβ this method is called to determine the name of the method that can process the request body. This method is expected not only to process the request body, but also to generate the response. So that's why we call this method the "response generator" forPUTrequests. π- Response generator method call β next, the response generator gets called. For
PUTrequests, the response generator is determined fromcontent_types_acceptedbased on the Here again, the method referred to bycontent_types_providedis not called by Web::Machine, but the response generator is free to callcontent_types_providedand find out the method itself, and call it. Or do something else.- When
resource_existsis true, the response generator is called fromo14in Web::Machine::FSM::States.
- When
Whenever a new resource gets created, a Location header is added to the response, carrying the URI path of the new resource. π
In general, we understand PUT to be a request to write to a resource. Typically that means either creating (INSERT) or modifying (UPDATE) one or more database records/objects.
So therefore, it has to be possible for a URI to resolve to a resource that doesn't exist yet. For example:
PUT employee/nick/Bubba
There may or may not be an employee named Bubba in the database, bestie. Nobody knows. But if we've got a resource called employee/nick/:nick, Path::Router will match it inside allowed_methods, and then the resource handler gets called in resource_exists β and up to this exact point, the same sequence of method calls is used for GET, POST, PUT, and DELETE. Same vibe, same path, no favorites. π
Web::MREST has no way of knowing whether an employee named Bubba exists. That's the handler's whole job: figure it out, then do an INSERT or an UPDATE as appropriate. This operation isn't expected to fail, but if it does fail, the handler should force a 4xx or 5xx status code (and provide an explanation) by calling $self->mrest_declare_status.
If the request causes a new object β and, hence, a new resource β to be created, the handler should cause a Location header with the URI of the new resource to be added to the response. That tells Web::Machine to set the response status to 201 Created. β¨ New resource, who dis.
If the request only modifies an existing object/resource, simply do not add a Location header to the response. That'll make Web::Machine hand back a 200 OK status. π«Ά
β¨ DELETE β¨
For DELETE, two methods get called: delete_resource and delete_completed. The delete_resource method should enact the delete operation and generate the response entity. The second one, delete_completed, is for the cases where the delete operation cannot be guaranteed to have completed β it defaults to false, but if it returns true, Web::Machine will trigger a ... response. π (Yes, that's literally what it says.)
π Resource does not exist π
GET
The request goes to finalization with 404 status. Bye. π
POST
The request goes to allow_missing_post, which always returns false in Web::MREST's implementation.
After that, the request goes to finalization with 404 status.
If the
PUT
π¦ finish_request π¦
The previous sections should suffice for the reader to gain a degree of understanding of how the state machine works for various types of requests, and how Web::MREST interfaces with the response handlers. (That's you, reader β you're doing amazing sweetie.)
The last cog of the FSM is finish_request.
π IN-DEPTH DISCUSSIONS OF VARIOUS TOPICS π
π§ Resource definitions π§
Like we read in the "crash course" above, resources are central to what a REST server is and does: the server processes incoming requests. Each request has a URI which resolves (or does not resolve) to a resource. Resources are defined as module variables β each module that contains resource handlers should also define a module variable (via our $resource_defs = { ... };) containing the definitions of the resources covered by that module. π
The top-level dispatch module, Web::MREST::Dispatch, should implement a method called init_router which calls the function
Web::MREST::InitRouter::load_resource_defs
for all the resource-defining modules. When the first HTTP request comes in, Web::MREST::Resource calls the init_router method. This only happens once, ensuring that the resource definitions are fully loaded for the first β and all subsequent β requests. No cap. β
Each resource definition is a hashref consisting of a number of properties. This definition hashref is itself included in the $resources package hashref, which essentially looks like this:
{
RESOURCE_NAME => RESOURCE_DEFINTION,
RESOURCE_NAME => RESOURCE_DEFINTION,
RESOURCE_NAME => RESOURCE_DEFINTION,
}
where RESOURCE_NAME is a resource name (a string like '/' or 'docu/text') and RESOURCE_DEFINITION is that resource's definition hashref.
The root resource should be defined under the name '/' and top-level resources should have a parent property set to this string.
In the resource definition, properties can be specified either as a scalar value, in which case the definition applies to all the methods specified in $site->MREST_SUPPORTED_HTTP_METHODS, or as a hashref in case the given resource is only defined for certain methods.
In the latter case, it is not necessary to define all properties as hashrefs. The set of permitted methods will always be taken from the handler property. For example in this snippet whizzo_resource is only defined for the GET method, and that will be applied to 'foo' (and the rest of this resource's properties) as well. π
'whizzo_resource' => {
'handler' => {
'GET' => 'some_method',
},
'foo' => 'barbazbat',
...
}
So 'foo' will only be defined for the GET method.
Examples:
'foo_prop' => 'value applied to all available methods',
'bar_prop' => {
'GET' => 'value applied to GET requests',
'POST' => 'value applied to POST requests',
},
There is one required property, 'handler', which is used to specify the handler(s) for the resource (see the examples below). The value of this property is taken to be the name of a method. This method call looks like this:
$self->$handler
and is located in Web::MREST::Resource->resource_exists
(The inheritance chain is set up in bin/mrest β the server startup script β and via use parent statements in the various modules that make up the inheritance chain.)
In addition, each resource may have any properties you, the application developer, wish to invest in it. For our 'docu' methods we use the properties 'description' and 'documentation', for example. β¨
Two properties β 'parent' and 'validations' β are exceptions to the above and should never be defined on a per-method basis:
'validations'contains validation checks to be applied when matching URI to resource (for more information, see the Path::Router documentation).'parent'contains the name of the resource's parent resource (defaults to''β the root resource)'documentation'is reserved for the self-documentation feature
π Path::Router object initialization π
When the server starts, the MREST_RESOURCE_DEFINITIONS and MREST_ROOT_RESOURCE meta parameters are initialized from the configuration file config/dispatch_MetaConfig.pm in the Web::MREST distribution.
The application developer will of course want to define her own set of resources. This should be done by manipulating the meta parameters MREST_RESOURCE_DEFINITIONS and MREST_ROOT_RESOURCE. A good place to do this is in the application's mrest_init_router routine. π¦
Here are two approaches to defining the application's resources, depending on whether the application wishes to retain the Web::MREST resources.
- retain
package MyApp::Resource;
use Clone 'clone';
use parent 'Web::MREST::Resource';
# We assume that the application somehow loads its resource definitions
# (including the root resource) into a package variable $r_defs -- for
# example by hard-coding them like this
my $r_defs = { ... };
# ----------------------------------------
# mrest_init_router - called by Web::MREST
# ----------------------------------------
sub mrest_init_router {
my $self = shift;
# set up the root resource
$meta->set( 'MREST_ROOT_RESOURCE', $r_defs->{''} );
delete $r_defs->{''};
# set up the remaining resources, retaining (but possibly
# overwriting) the Web::MREST default resources
my $mrest_defs = clone( $meta->MREST_RESOURCE_DEFINITIONS );
foreach my $r_name ( keys %$r_defs ) {
$mrest_defs->{$r_name} = $r_defs->{$r_name};
}
$meta->set( 'MREST_RESOURCE_DEFINITIONS', $mrest_defs );
}
- do not retain
This approach is more simple because no mrest_init_router need be written. The application should have its own distro sharedir config/ and therein a file dispatch_MetaConfig.pm. Inside that file, the application puts its own resource definitions in the MREST_RESOURCE_DEFINITIONS and MREST_ROOT_RESOURCE parameters (refer to config/dispatch_MetaConfig.pm in the Web::MREST distribution for syntax and semantics).
The application's definitions will overlay (i.e. replace) those of Web::MREST. Even in this scenario, some or all of Web::MREST's resources could be used in the application, but only by copy-pasting the definitions and their respective handlers into the application's source code. π
πΈ Tree structure πΈ
Web::MFILE allows resources to be defined in a tree structure. It is designed to allow a tree structure to be described in a flat configuration file. The MREST_RESOURCE_DEFINITIONS hash is keyed on the resource name. Child resources are indicated by including a parent property with the name of the parent resource. Care should be exercised not to introduce any circular references. π
If a flat structure is desired, simply do not include any parent properties in your resource definitions.
The format of MREST_RESOURCE_DEFINITIONS hash is documented in config/dispatch_MetaConfig.pm.
π $Web::MREST::InitRouter::resources π
The resource definition hashrefs in the dispatch modules are designed to be written and maintained by humans. When the init_router method runs, it loops over all the resource definitions and builds up a second hash, $Web::MREST::InitRouter::resources, which contains the same information in a format that is more convenient for automated processing. π§
Since the resource definitions are a potential source of typographical and semantic errors, you should dump this package variable to the log and examine it to make sure your resource definitions are being processed correctly. (Trust. Do the dump. Your future self says thank you.)
π Errors π
As we move through the state machine (i.e. the chain of method calls driven by Web::Machine), we build up a "context" from which we generate the HTTP response. Stated very simply, the response code can either be 'OK' (200) or "something else" β i.e., an error of some kind. π₯
And, indeed, checking for errors accounts for a large portion of what our resource modules do. As RFC2616 explains, errors can be divided into two classes: client errors and server errors.
π Client errors (4xx) π
Client errors have status codes that start with 4 (e.g. 400, 401, 404).
RFC2616 has this to say about them:
The 4xx class of status code is intended for cases in which the client
seems to have erred. Except when responding to a HEAD request, the server
SHOULD include an entity containing an explanation of the error situation, and
whether it is a temporary or permanent condition. These status codes are
applicable to any request method. User agents SHOULD display any included
entity to the user.
Server errors (5xx) π₯
okay so server errors are the ones where the code starts with the digit 5. and according to RFC2616, they:
indicate cases in which the server is aware that it has erred or is incapable of performing the request. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. User agents SHOULD display any included entity to the user. These response codes are applicable to any request method.
and the tea is: you can't just yeet a bare 4xx or 5xx status code and call it a day. bestie, the response should include an entity body that explains what went wrong. like, give us the story. π
How to provide explanation in response entity π
so Web::MREST has your back with a whole mechanism for adding that explanation to the entity body, exactly like RFC2616 demands. at the exact spot in your resource module where you realize something's gone sideways, do this:
$self->mrest_declare_status( code => '400', explanation => 'You messed up' );
this gets turned into the respective App::CELL::Status object and comes back in the response entity. the object is gonna look something like this:
{
level => 'ERR',
code => 'You messed up',
payload => {
http_code => '500',
uri_path => ... (taken from the context),
resource_name => ... (taken from the context),
found_in => ... (taken from 'caller'),
permanent => JSON::true (the default),
},
}
or, if you're feeling fancy, you can pass in your own custom App::CELL::Status object. π
to see how the App::CELL::Status object becomes the response entity, peep the finish_request method in Web::MREST::Resource.
Context π¦
usually just called $context, the "MREST context" is a hashref that gets built up while request processing is happening. besides being used inside Web::MREST::Resource, it's always sent as an argument whenever Web::MREST::Resource calls a hook. so you, the developer, can mess with it in your implementations of the various hook routines. it's like a little backstage pass. ποΈ
Authentication π
ever since the Big Bad Wolf ate Granny, authentication mechanisms have been prone to abuse by individuals who are willing to lie about their identity. πΊπ΅
humans are actually pretty good at telling one human from another, as long as they can use all their senses. computers? not so much. they lack proper senses and are downright awful at this task. computerized authentication schemes usually work by making the user jump through one or more hoops. whoever makes it through is deemed to be the user. what could possibly go wrong? π
passwords (or passphrases) are the "hoop" most frequently used to authenticate users and keep would-be intruders out. so a system's security is often judged by how well it protects user credentials from disclosure. since usernames are public, the only thing keeping a determined intruder at bay is the password β and various measures are taken to protect them. π‘οΈ
from the perspective of Web::Machine, authentication is all about calling the is_authorized method. if it returns false, the response will be 401 Unauthorized. if it returns true, request processing continues. whatever authentication measures you decide to implement should be triggered by this method call. π¦
for more about is_authorized, check out the Web::Machine::Resource documentation at <https://metacpan.org/pod/Web::Machine::Resource#is_authorized-authorization_header>. (iykyk) π
Authorization π«
once authentication has figured out who the user is, a related task, authorization, kicks in. as the name suggests (and the RFC's vague use of the term "authorization" notwithstanding), authorization answers the question:
Is this specific user authorized to make this request?
compare that with authentication, which answers a totally different question:
Is this user really who they are purporting to be?
or, even more pithily:
Who is this user?
authorization implies a boolean "function" (in both the mathematical and computer science sense) that takes three arguments: the username, the HTTP method, and the resource. implementation of this function is left to you, the application developer. have fun! π
it's worth noting here that Web::Machine provides a forbidden method. since is_authorized is already taken for authentication, we can use forbidden for authorization. just be sure to understand thoroughly that a true return value from forbidden means "not authorized". like, don't get it twisted. π
βοΈ
Customized URI parsing π£οΈ
while Web::MREST provides for URI parsing using Path::Router, if that's not your vibe, you can parse URIs yourself by simply substituting your own init_router and match methods for the ones provided by Path::Router and Path::Router::Route::Match, respectively.
when request processing enters resource_exists, alternatively, you can overlay the init_router routine with one that returns an arbitrary object (stored in $router) that has a match method. after that, Web::MREST does:
my $match = $router->match( $path );
where $path is the relative portion of the URI (i.e. everything left after the http://myapp.example.com/ part is cut off).
the $match object should provide a route method, which should return the definition of the matched resource. see "RESOURCE DEFINITIONS". π
FUNCTIONS IN THIS MODULE π§°
init βοΈ
do initialization-like things, such as loading configuration parameters. takes a PARAMHASH which can contain one of the following:
"distro"β the name of the application distribution from which the distro sharedir will be loaded."path"β the name (full path) of a directory containing the application's configuration files."hashref"β a reference to a hash containing meta parameters to be loaded.
version π·οΈ
accessor method (to be called like a constructor) providing access to $VERSION variable.
and that's a wrap, bestie! this was Web::MREST(3pm) from perl v5.40.1, dated 2025-06-21. slay! πβ¨
π¦ where this page comes from
- ubuntu resolute: libweb-mrest-perl 0.290-2 Β· BSD-3-Clause
- ubuntu stonking: libweb-mrest-perl 0.290-2 Β· BSD-3-Clause
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms