| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# ************************************************************************* |
|
2
|
|
|
|
|
|
|
# Copyright (c) 2014-2015-2015, SUSE LLC |
|
3
|
|
|
|
|
|
|
# |
|
4
|
|
|
|
|
|
|
# All rights reserved. |
|
5
|
|
|
|
|
|
|
# |
|
6
|
|
|
|
|
|
|
# Redistribution and use in source and binary forms, with or without |
|
7
|
|
|
|
|
|
|
# modification, are permitted provided that the following conditions are met: |
|
8
|
|
|
|
|
|
|
# |
|
9
|
|
|
|
|
|
|
# 1. Redistributions of source code must retain the above copyright notice, |
|
10
|
|
|
|
|
|
|
# this list of conditions and the following disclaimer. |
|
11
|
|
|
|
|
|
|
# |
|
12
|
|
|
|
|
|
|
# 2. Redistributions in binary form must reproduce the above copyright |
|
13
|
|
|
|
|
|
|
# notice, this list of conditions and the following disclaimer in the |
|
14
|
|
|
|
|
|
|
# documentation and/or other materials provided with the distribution. |
|
15
|
|
|
|
|
|
|
# |
|
16
|
|
|
|
|
|
|
# 3. Neither the name of SUSE LLC nor the names of its contributors may be |
|
17
|
|
|
|
|
|
|
# used to endorse or promote products derived from this software without |
|
18
|
|
|
|
|
|
|
# specific prior written permission. |
|
19
|
|
|
|
|
|
|
# |
|
20
|
|
|
|
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
21
|
|
|
|
|
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
22
|
|
|
|
|
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
23
|
|
|
|
|
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
24
|
|
|
|
|
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
25
|
|
|
|
|
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
26
|
|
|
|
|
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
27
|
|
|
|
|
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
28
|
|
|
|
|
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
29
|
|
|
|
|
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
30
|
|
|
|
|
|
|
# POSSIBILITY OF SUCH DAMAGE. |
|
31
|
|
|
|
|
|
|
# ************************************************************************* |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
package Web::MREST; |
|
34
|
|
|
|
|
|
|
|
|
35
|
22
|
|
|
22
|
|
208162
|
use 5.012; |
|
|
22
|
|
|
|
|
81
|
|
|
36
|
22
|
|
|
22
|
|
107
|
use strict; |
|
|
22
|
|
|
|
|
47
|
|
|
|
22
|
|
|
|
|
447
|
|
|
37
|
22
|
|
|
22
|
|
105
|
use warnings; |
|
|
22
|
|
|
|
|
46
|
|
|
|
22
|
|
|
|
|
632
|
|
|
38
|
|
|
|
|
|
|
|
|
39
|
22
|
|
|
22
|
|
121
|
use App::CELL qw( $CELL $log $meta $core $site ); |
|
|
22
|
|
|
|
|
39
|
|
|
|
22
|
|
|
|
|
2990
|
|
|
40
|
22
|
|
|
22
|
|
154
|
use App::CELL::Test qw( _touch ); |
|
|
22
|
|
|
|
|
54
|
|
|
|
22
|
|
|
|
|
967
|
|
|
41
|
22
|
|
|
22
|
|
289
|
use Data::Dumper; |
|
|
22
|
|
|
|
|
56
|
|
|
|
22
|
|
|
|
|
864
|
|
|
42
|
22
|
|
|
22
|
|
220
|
use File::ShareDir; |
|
|
22
|
|
|
|
|
46
|
|
|
|
22
|
|
|
|
|
773
|
|
|
43
|
22
|
|
|
22
|
|
579
|
use Log::Any::Adapter; |
|
|
22
|
|
|
|
|
755
|
|
|
|
22
|
|
|
|
|
208
|
|
|
44
|
22
|
|
|
22
|
|
589
|
use Params::Validate qw( :all ); |
|
|
22
|
|
|
|
|
37
|
|
|
|
22
|
|
|
|
|
3401
|
|
|
45
|
|
|
|
|
|
|
#use Try::Tiny; |
|
46
|
22
|
|
|
22
|
|
652
|
use Web::Machine; |
|
|
22
|
|
|
|
|
181430
|
|
|
|
22
|
|
|
|
|
12007
|
|
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head1 NAME |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Web::MREST - Minimalistic REST server |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 VERSION |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
Version 0.288 |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=cut |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
our $VERSION = '0.288'; |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=head2 Development status |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
L<Web::MREST> is currently in "Alpha - feature freeze". There are almost |
|
70
|
|
|
|
|
|
|
certainly bugs lurking in the code, but all features have been implemented. |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
To take this module for a spin, execute this command: |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
$ mrest Web-MREST Web::MREST::Dispatch |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Leave this running, and from another console start the command-line client: |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
$ mrest-cli |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
In the CLI client, type e.g. |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Web::MREST::CLI::Parser> get / |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
A 'GET' request will be sent for the root resource and the CLI client |
|
90
|
|
|
|
|
|
|
will display a representation of the response. |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
A similar result can be obtained using C<curl>: |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
curl -v http://localhost:5000/ -X GET -H "Content-Type: application/json" |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
MREST stands for "minimalistic" or "mechanical" REST server. (Mechanical because |
|
102
|
|
|
|
|
|
|
it relies on L<Web::Machine>.) |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
L<Web::MREST> provides a fully functional REST server that can be started |
|
105
|
|
|
|
|
|
|
with a simple command. Without modification, the server provides a set of |
|
106
|
|
|
|
|
|
|
generalized resources that can be used to demonstrate how the REST server |
|
107
|
|
|
|
|
|
|
works, or for testing. |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Developers can use L<Web::MREST> as a platform for implementing their own |
|
110
|
|
|
|
|
|
|
REST servers, as described below. L<App::Dochazka::REST> is a "real-world" |
|
111
|
|
|
|
|
|
|
example of such a server. |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
For an introduction to REST and Web Services, see |
|
114
|
|
|
|
|
|
|
L<Web::MREST::WebServicesIntro>. |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=head1 RFC2616 AS A STATE MACHINE |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
RFC2616 is, of course, the HTTP 1.1 standard - not a state machine. But |
|
121
|
|
|
|
|
|
|
the authors of "Web Machine" (which was originally implemented in Erlang) had a |
|
122
|
|
|
|
|
|
|
neat idea to represent it as a state machine and use this to implement a server |
|
123
|
|
|
|
|
|
|
for providing web services. |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
L<Web::Machine> is, of course, the Perl port of Web Machine. |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
L<Web::MREST> relies on L<Web::Machine> to implement RFC2616. L<Web::MREST> |
|
128
|
|
|
|
|
|
|
can be thought of as an additional abstraction layer over L<Web::Machine>. |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
By itself, L<Web::Machine> is not a server. It does not listen on a port, for |
|
131
|
|
|
|
|
|
|
example. Instead, it is designed to work (via L<Plack>) with a |
|
132
|
|
|
|
|
|
|
L<PSGI>-compliant web server. |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
The web server hands incoming requests over to L<Web::Machine>, which runs the |
|
135
|
|
|
|
|
|
|
requests through its state machine. (The L<Web::Machine> authors refer to the |
|
136
|
|
|
|
|
|
|
state machine as "the FSM.") The best way to grasp the state machine is to |
|
137
|
|
|
|
|
|
|
envision it as a flow-chart. At each "decision node" of the flow-chart - where |
|
138
|
|
|
|
|
|
|
flow can go in one of two directions - L<Web::Machine> calls the method |
|
139
|
|
|
|
|
|
|
corresponding to that node. Each node is designated by a letter and a number: |
|
140
|
|
|
|
|
|
|
e.g. F7, O18, etc. |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
The flow-chart implemented by the FSM can be found L<here|http://...> - you are |
|
143
|
|
|
|
|
|
|
encouraged to have that open for reference while reading this documentation |
|
144
|
|
|
|
|
|
|
and implementing your REST server. |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head1 SERVER STARTUP AND INHERITANCE SCHEME |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=head2 Standalone mode |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
As stated above, L<Web::MREST> is capable of operating independently. To try |
|
155
|
|
|
|
|
|
|
it out, start up the server like this: |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
$ mrest-standalone |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
And then point your browser to |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
http://localhost:5000 |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
If you look inside the C<mrest-standlone> script, you will see that it is |
|
164
|
|
|
|
|
|
|
just a wrapper for the C<mrest> script, which takes two mandatory options. The |
|
165
|
|
|
|
|
|
|
first, C<--distro>, is the name of the distribution in whose sharedir it should |
|
166
|
|
|
|
|
|
|
look for configuration files. The second, C<--module>, is the name of the |
|
167
|
|
|
|
|
|
|
application's resource module, i.e. the ultimate module in the chain of |
|
168
|
|
|
|
|
|
|
inheritance. |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
In standalone mode, the actual command that is run is: |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
mrest --distro=Web::MREST --module=Web::MREST::Dispatch |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
which causes the chain of inheritance to be built up as follows: |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=over |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=item C<bin/mrest> |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
calls C<< Web::Machine->new >>; the L<Web::Machine> object is blessed into L<Web::MREST::Dispatch> |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=item L<Web::MREST::Dispatch> |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
inherits from L<Web::MREST::Entity> |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=item L<Web::MREST::Entity> |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
inherits from L<Web::MREST::Resource> |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=item L<Web::MREST::Resource> |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
inherits from L<Web::Machine::Resource> |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=back |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
When you browse to C<http://0:5000> in standalone mode, you get a list of the |
|
197
|
|
|
|
|
|
|
sample REST resources that are available. For more information on these, see |
|
198
|
|
|
|
|
|
|
C<config/dispatch_Config.pm>. |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head2 With your application |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
Starting the server with your application is the same as described in |
|
204
|
|
|
|
|
|
|
L<"Standalone mode">, above, except that you replace C<Web-MREST> with the name |
|
205
|
|
|
|
|
|
|
of your distribution and C<Web::MREST::Dispatch> with the name of your ultimate |
|
206
|
|
|
|
|
|
|
resource module. |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
$ mrest YourApp-MREST YourApp::MREST::Dispatch |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
For example, here we are starting the server with the distribution |
|
211
|
|
|
|
|
|
|
C<YourApp-MREST>, which is presumed to implement a chain of inheritance |
|
212
|
|
|
|
|
|
|
similar to L<Web::MREST>'s, i.e.: |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Web::MREST -> YourApp::MREST::Resource -> YourApp::MREST::Dispatch |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
Thanks to this arrangement, the application developer can customize |
|
217
|
|
|
|
|
|
|
L<Web::MREST> - i.e., not only providing her own resources and handlers, |
|
218
|
|
|
|
|
|
|
but even altering how the state machine operates, if necessary - by providing |
|
219
|
|
|
|
|
|
|
her own chain of inheritance and overriding various methods within it. |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=head3 Recapitulation |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Since the above is quite important, let's go over it again: |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
The L<Web::MREST> documentation will always refer to your application either |
|
227
|
|
|
|
|
|
|
as the "application" or as C<YourApp>. The application should take the form |
|
228
|
|
|
|
|
|
|
of a Perl distribution, which should have: |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=over |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=item * a distribution sharedir |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=item * a resource module, C<YourApp::Resource>. |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=item * a dispatch module, C<YourApp::Dispatch> |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=back |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
For now, just think of these three components as "black boxes". We will |
|
241
|
|
|
|
|
|
|
cover their contents later. |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
The server (i.e. your application), is started by executing the C<mrest> |
|
244
|
|
|
|
|
|
|
executable with the name of your application's distribution and the name of its |
|
245
|
|
|
|
|
|
|
dispatch module, which should be the ultimate module in the chain of |
|
246
|
|
|
|
|
|
|
inheritance. |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
$ perl mrest YourApp YourApp::Dispatch |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Under the hood the startup script, which can be reviewed at C<bin/mrest>, |
|
251
|
|
|
|
|
|
|
does essentially this: |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
use Web::Machine; |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
Web::Machine->new( |
|
256
|
|
|
|
|
|
|
resource => 'YourApp::Dispatch', |
|
257
|
|
|
|
|
|
|
)->to_app; |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
There are two key points concerning the L<Web::Machine> object constructed by |
|
260
|
|
|
|
|
|
|
call to C<< Web::Machine->new >>: |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=over |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=item 1. the object is blessed into C<YourApp::Dispatch> |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=item 2. the object is a L<Plack> application |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=back |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=head1 INHERITANCE SCHEME |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
As seen in the previous section, C<YourApp> inherits from |
|
275
|
|
|
|
|
|
|
L<Web::MREST> via a chain of inheritance. Here is the chain implemented by L<Web::MREST>: |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
-> Web::MREST::Dispatch |
|
279
|
|
|
|
|
|
|
-> Web::MREST::Entity |
|
280
|
|
|
|
|
|
|
-> Web::MREST::Resource |
|
281
|
|
|
|
|
|
|
-> Web::Machine::Resource |
|
282
|
|
|
|
|
|
|
-> Plack::Component |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Assuming L<YourApp> has its authentication and authorization routines |
|
285
|
|
|
|
|
|
|
in L<YourApp::Resource> and its resource definitions and handlers in |
|
286
|
|
|
|
|
|
|
L<YourApp::Dispatch>, the chain for L<YourApp> would look like this: |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
-> YourApp::Dispatch |
|
289
|
|
|
|
|
|
|
-> YourApp::Resource |
|
290
|
|
|
|
|
|
|
-> Web::MREST::Entity |
|
291
|
|
|
|
|
|
|
-> Web::MREST::Resource |
|
292
|
|
|
|
|
|
|
-> Web::Machine::Resource |
|
293
|
|
|
|
|
|
|
-> Plack::Component |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
(In other words, L<YourApp::Dispatch> and L<YourApp::Resource> replace |
|
296
|
|
|
|
|
|
|
L<Web::MREST::Dispatch>, which is just a demo.) |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
When L<Web::Machine> reaches a given node in the FSM, it calls the |
|
299
|
|
|
|
|
|
|
corresponding method on that L<Web::Machine> object. Since the object is |
|
300
|
|
|
|
|
|
|
blessed into C<YourApp::Dispatch>, that module is where Perl will |
|
301
|
|
|
|
|
|
|
start to look for the method. |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
If the method is not found at the lowest level, Perl follows the chain of |
|
304
|
|
|
|
|
|
|
inheritance "upward". The highest level, L<Plack::Component>, is shown only for |
|
305
|
|
|
|
|
|
|
completeness - L<Web::MREST::Resource> and L<Web::MREST::Entity> implement |
|
306
|
|
|
|
|
|
|
all the methods that your resource module might (or should) want to override. |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
Readers who are not well-versed in writing Perl applications that use |
|
309
|
|
|
|
|
|
|
inheritance are referred to the fine Perl manuals such as C<perlootut>. |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=head1 STATE MACHINE INTRODUCTION |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
At this point we have enough background information to begin to grasp the state |
|
316
|
|
|
|
|
|
|
machine. (Instead of writing "state machine" we will follow the L<Web::Machine> |
|
317
|
|
|
|
|
|
|
convention of referring to it as the "FSM".) This section presents selected |
|
318
|
|
|
|
|
|
|
features and nodes of the FSM, how L<Web::MREST> implements them, and how to |
|
319
|
|
|
|
|
|
|
use them. The discourse proceeds in the order in which the methods are called |
|
320
|
|
|
|
|
|
|
when an HTTP request enters the FSM. We can envision these method calls as |
|
321
|
|
|
|
|
|
|
decision nodes of a flow-chart, or "cogs" of the FSM. |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
And we needn't just imagine the flow-chart - it actually exists and can be |
|
324
|
|
|
|
|
|
|
downloaded from L<...>. If you want to understand how L<Web::Machine> and |
|
325
|
|
|
|
|
|
|
L<Web::MREST> work, this document is of fundamental importance. Hereinafter |
|
326
|
|
|
|
|
|
|
it will be referred to as "the FSM diagram". |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
As you can see in the FSM diagram, each FSM cog has a code like C<B6>, for ease |
|
329
|
|
|
|
|
|
|
of reference. |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=head1 POLICIES AND FEATURES |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
L<Web::Machine> implements the FSM, and that's all it does. In particular, it |
|
336
|
|
|
|
|
|
|
imposes no policies on distributions that use it. By taking this approach, |
|
337
|
|
|
|
|
|
|
L<Web::Machine> maximizes its range of potential uses. |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Powerful as it is, L<Web::Machine> can be confusing to use. When I started |
|
340
|
|
|
|
|
|
|
writing my first application based on it, I found myself wanting an |
|
341
|
|
|
|
|
|
|
intermediate module between my application and L<Web::Machine> - something that |
|
342
|
|
|
|
|
|
|
would make L<Web::Machine> a little more friendly. |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
L<Web::MREST> is that module. It builds on L<Web::Machine> in an effort to |
|
345
|
|
|
|
|
|
|
provide certain additional features. Inevitably, this means imposing some |
|
346
|
|
|
|
|
|
|
policies (i.e., limitations) on users. To me that seems like an acceptable |
|
347
|
|
|
|
|
|
|
trade-off. |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head2 Path dispatch |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
A key part of any web application is "path dispatch" (i.e. URI translation), |
|
353
|
|
|
|
|
|
|
which answers the question: "how are URIs mapped to resources?" |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Although L<Web::Machine> provides a way to specify handlers for various media |
|
356
|
|
|
|
|
|
|
types that may appear in request and response entities, it provides no |
|
357
|
|
|
|
|
|
|
way of getting from the URI to the handler. L<Web::MREST> bridges this gap |
|
358
|
|
|
|
|
|
|
by providing a system of resource definitions (see L<"Resource definitions">, |
|
359
|
|
|
|
|
|
|
below). |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
The definition of each resource specifies the URI-to-resource mapping and |
|
362
|
|
|
|
|
|
|
provides the name of the resource's handler method. Internally, L<Web::MREST> |
|
363
|
|
|
|
|
|
|
uses a single L<Path::Router> object to parse URIs. |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
Before any URIs can be parsed, this L<Path::Router> object must be initialized. |
|
366
|
|
|
|
|
|
|
This is done in L<Web::MREST::Resource>, in the C<service_available> method. |
|
367
|
|
|
|
|
|
|
That method checks the scalar variable that is supposed to contain the |
|
368
|
|
|
|
|
|
|
L<Path::Router> object and, if needed, calls the C<init_router> method to |
|
369
|
|
|
|
|
|
|
initialize it. |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
In the L<Web::MREST> demo application, C<init_router> is implemented in |
|
372
|
|
|
|
|
|
|
L<Web::MREST::Dispatch>. |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=head2 Resource handlers |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
The L<Web::Machine> documentation mentions "handlers" but doesn't go into |
|
378
|
|
|
|
|
|
|
any detail on how to write them. L<Web::MREST> not only provides some working |
|
379
|
|
|
|
|
|
|
resource handlers, but also implements a paradigm for writing them. |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
In this paradigm, the handler is called as a method, just like any of the other |
|
382
|
|
|
|
|
|
|
methods in the chain of inheritance. (To avoid namespace issues, it is |
|
383
|
|
|
|
|
|
|
recommended that handler method names start with C<handler_>.) The name of the |
|
384
|
|
|
|
|
|
|
method is specified in the resource definition. |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
The handler method is called twice - in other words, there are two passes. In |
|
387
|
|
|
|
|
|
|
the first pass, the handler is called with the argument C<1> (scalar value) and |
|
388
|
|
|
|
|
|
|
is expected to return a boolean value indicating whether the resource exists. |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
In the second pass, indicated by the argument C<2> (scalar value), the handler |
|
391
|
|
|
|
|
|
|
is expected to return a C<App::CELL::Status> object. This object (rendered in |
|
392
|
|
|
|
|
|
|
JSON) becomes the response entity unless overrided by a declared status (see |
|
393
|
|
|
|
|
|
|
C<mrest_declare_status> in L<Web::MREST::Resource>. |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
B<N.B.:> The request entity is not available to the handler (via |
|
396
|
|
|
|
|
|
|
C<$self->context->{request_entity}> until the second pass! |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=head2 Status objects |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
As mentioned in the previous section, L<App::CELL::Status> objects are returned |
|
402
|
|
|
|
|
|
|
by resource handlers. Not only that - L<Web::MREST> tries its best to I<always> |
|
403
|
|
|
|
|
|
|
return an L<App::CELL::Status> object in the response entity. |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
Actually, it is not the object itself that is returned, but a JSON |
|
406
|
|
|
|
|
|
|
representation of its underlying data structure. From this, the object can |
|
407
|
|
|
|
|
|
|
easily be reconstituted on the client side by doing |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
my $status = $JSON->decode( $response_entity ); |
|
410
|
|
|
|
|
|
|
bless $status, 'App::CELL::Status'; |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
For more on what status objects can do, see L<App::CELL::Status>, L<App::CELL>, |
|
413
|
|
|
|
|
|
|
and L<App::CELL::Guide>. |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
=head2 Error statuses |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
L<Web::Machine> always tries to return the proper HTTP status code in the |
|
419
|
|
|
|
|
|
|
response. The application developer will likely need to "force" a code in |
|
420
|
|
|
|
|
|
|
certain cases. For example, the request may be "malformed" in a way that is |
|
421
|
|
|
|
|
|
|
not discoverable until the handler runs. Or, caught exceptions may need to be |
|
422
|
|
|
|
|
|
|
exposed to the client with C<500 - Internal Error>. |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
Also, the RFC says |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
. . . the server SHOULD include an entity containing an explanation of the |
|
427
|
|
|
|
|
|
|
error situation, and whether it is a temporary or permanent condition. |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
Clearly, then, a mechanism is needed for providing such explanations and |
|
430
|
|
|
|
|
|
|
indicating whether the error is temporary or permanent. And that mechanism |
|
431
|
|
|
|
|
|
|
should enable an arbitrary status code to be declared. |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
By itself, L<Web::Machine> does not really provide such a mechanism. What it |
|
434
|
|
|
|
|
|
|
does provide is a mechanism for "forcing" an arbitrary status code (e.g. C<404 |
|
435
|
|
|
|
|
|
|
- Not Found>) by returning a scalar reference. This mechanism has two |
|
436
|
|
|
|
|
|
|
disadvantages: |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=over |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=item it is only available at certain junctions of the FSM |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
I wanted a way to "declare" a status code at any point and be certain that |
|
443
|
|
|
|
|
|
|
L<Web::Machine> won't change it later on. |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=item there is no obvious way to provide an explanation of the error |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
L<Web::Machine> considers this an implementation detail. |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=back |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
Hence, L<Web::MREST> provides the C<mrest_declare_status> method. To learn |
|
452
|
|
|
|
|
|
|
how to call it and how it works, see L<Web::MREST::Resource>. |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=head1 THE FINE STATE MACHINE |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
In this section we take a detailed look at the FSM by considering some common |
|
459
|
|
|
|
|
|
|
scenarios. For our purposes these are C<GET>, C<POST>, C<PUT>, and C<DELETE> |
|
460
|
|
|
|
|
|
|
requests. Handling can differ according to whether or not a C<POST> creates a |
|
461
|
|
|
|
|
|
|
new resource and whether or not the resource is determined to exist. |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=head2 Part One (sanity checks and information gathering) |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
The first few cogs are executed, in the same order, on all requests regardless |
|
466
|
|
|
|
|
|
|
of method. They can be thought of both as a set of sanity checks and as an |
|
467
|
|
|
|
|
|
|
information-gathering process. |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=head3 C<service_available> (B13) |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
The first method call is C<service_available>, which is implemented by |
|
473
|
|
|
|
|
|
|
L<Web::MREST::Resource> and should I<not> be implemented by your application, |
|
474
|
|
|
|
|
|
|
because it calls C<init_router> to ensure that all the resource definitions are |
|
475
|
|
|
|
|
|
|
loaded and the L<Path::Router> singleton is properly initialized. |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
This is not really a limitation, however. Whatever code you need to run here |
|
478
|
|
|
|
|
|
|
can be placed in a method called C<mrest_service_available>, which should |
|
479
|
|
|
|
|
|
|
return a boolean value (i.e. 1 or 0), which determines the return value from |
|
480
|
|
|
|
|
|
|
the method. |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
If the service really isn't available, you can return false, which will trigger |
|
483
|
|
|
|
|
|
|
a C<503 Service Not Available> response. Before returning you should do: |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
$self->mrest_declare_status( explanation => '...', permanent => 0 ); |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
to provide an explanation of what is going on. |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
For details, see the C<t/503-Service-Unavailable.t> unit test. |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=head3 C<known_methods> (B12) |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
Returns the list of supported ("known") methods in |
|
495
|
|
|
|
|
|
|
C<< $site->MREST_SUPPORTED_HTTP_METHODS >>. If the request method is not |
|
496
|
|
|
|
|
|
|
in that list, a C<501 Not Implemented> response is returned along with |
|
497
|
|
|
|
|
|
|
an explanation that the method requested is not supported. |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
If this behavior is not appropriate, the method can be implemented by the |
|
500
|
|
|
|
|
|
|
application. |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
=head3 C<uri_too_long> (B11) |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
If the request URI is longer than the value set in the C<MREST_MAX_LENGTH_URI> site parameter, |
|
506
|
|
|
|
|
|
|
the client will receive a C<414 Request URI Too Long> response. |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
To override this behavior, provide your own C<uri_too_long> routine in your |
|
509
|
|
|
|
|
|
|
resource module. |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
This functionality is demonstrated by the C<t/414-Request-URI-Too-Long.t> unit. |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=head3 C<allowed_methods> (B10) |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
"Is the method allowed on this resource?" |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
This next routine is where things start to get complicated. According to the |
|
519
|
|
|
|
|
|
|
L<Web::Machine::Resource |
|
520
|
|
|
|
|
|
|
documentation|https://metacpan.org/pod/Web::Machine::Resource#allowed_methods>, |
|
521
|
|
|
|
|
|
|
we are expected to respond with a list of methods allowed on the resource. To |
|
522
|
|
|
|
|
|
|
assemble such a list, we must first answer two questions: |
|
523
|
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=over |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=item 1. Have the resource definitions been loaded? |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
=item 2. Does the URI match a known resource? |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=back |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
After the server starts, the first time this method is called triggers a |
|
533
|
|
|
|
|
|
|
call to the C<init_router> method, which populates the C<$resources> package |
|
534
|
|
|
|
|
|
|
variable in C<Web::MREST::InitRouter> with all the resource definitions. |
|
535
|
|
|
|
|
|
|
This is explained in detail in L<"Resource definitions">. This takes care of |
|
536
|
|
|
|
|
|
|
the first question. |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
The second question is answered by C<Path::Router>. Once the request has |
|
539
|
|
|
|
|
|
|
been associated with a known resource, completing our task becomes a matter of |
|
540
|
|
|
|
|
|
|
getting and returning the set of methods for which the resource is defined. |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=head3 C<malformed_request> (B9) |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
A true return value from this method triggers a "400 Bad Request" response |
|
546
|
|
|
|
|
|
|
status. RFC2616 does not stipulate exactly what constitutes a bad request. |
|
547
|
|
|
|
|
|
|
We already (in allowed_methods) took care of the case when the URI |
|
548
|
|
|
|
|
|
|
fails to match a known resource, and that includes applying any C<validations> |
|
549
|
|
|
|
|
|
|
properties from the resource definition. |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
So, in this method (or your overlay) we take the "next step" (whatever that is) |
|
552
|
|
|
|
|
|
|
in vetting the request. Keep in mind that this method is called before |
|
553
|
|
|
|
|
|
|
the resource handler. If you have any sanity checks you wish to apply _after_ |
|
554
|
|
|
|
|
|
|
the URI is matched to a resource but _before_ the resource handler fires, this |
|
555
|
|
|
|
|
|
|
is the place to put them. |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
If you would like to keep L<Web::MREST>'s implementation of this method |
|
558
|
|
|
|
|
|
|
(which, for example, pushes the Content-Length and Content-Type information |
|
559
|
|
|
|
|
|
|
onto the context) and add your own logic, you can put it in |
|
560
|
|
|
|
|
|
|
C<mrest_malformed_request> instead of overriding C<malformed_request> itself. |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
If you intend to return false from this method you should first do this: |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
$self->mrest_declare_status( explanation => '...' ); |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
to ensure that an explanation is included with the 400 response. |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=head3 C<is_authorized> (B8) |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
In my mind, "authentication" is the process of determining who the user |
|
572
|
|
|
|
|
|
|
is, and "authorization" determines if the user is allowed to do what she |
|
573
|
|
|
|
|
|
|
is asking to do. However, RFC2616 does not make such a clear distinction. |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
For that reason, it is left to the application to implement this method |
|
576
|
|
|
|
|
|
|
if needed. |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=head3 C<forbidden> (B7) |
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
The same thoughts as expressed under C<is_authorized>, above, apply to |
|
582
|
|
|
|
|
|
|
this method as well. |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=head3 C<valid_content_headers> (B6) |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
This is where you vet the C<Content-*> headers in the request. If the |
|
588
|
|
|
|
|
|
|
request contains any invalid C<Content-*> headers (i.e., if the '*' part |
|
589
|
|
|
|
|
|
|
does not appear in << $site->MREST_VALID_CONTENT_HEADERS >>), |
|
590
|
|
|
|
|
|
|
a 501 will be generated. |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
The content headers are passed to the method in a L<Hash::MultiValue> |
|
593
|
|
|
|
|
|
|
object. |
|
594
|
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=head3 C<known_content_type> (B5) |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
If the C<Content-Type> header is relevant - i.e., if this is a PUT or |
|
599
|
|
|
|
|
|
|
POST request and if there is a request entity - check it against |
|
600
|
|
|
|
|
|
|
<< $site->MREST_SUPPORTED_CONTENT_TYPES >>. |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=head3 C<valid_entity_length> (B4) |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
A simple routine that compares the entity length (in bytes) with the |
|
606
|
|
|
|
|
|
|
maximum set in C<< $site->MREST_MAX_LENGTH_REQUEST_BODY >>. |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=head3 C<options> (B3) |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
If your application needs to support the C<OPTIONS> method, you should |
|
612
|
|
|
|
|
|
|
implement this yourself - otherwise, ignore it. |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
=head2 Part Two (content negotioation) |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
The HTTP standard provides some complicated logic to enable clients |
|
618
|
|
|
|
|
|
|
and servers to "negotiate" the format (media type), language, encoding, |
|
619
|
|
|
|
|
|
|
etc. in which content will be passed back and forth. Here in the L<Web::MREST> |
|
620
|
|
|
|
|
|
|
documentation we gloss over this complexity and focus only on the media type. |
|
621
|
|
|
|
|
|
|
However, L<Web::Machine> includes methods for handling all the content |
|
622
|
|
|
|
|
|
|
negotiation decision nodes and the application developer is free to take |
|
623
|
|
|
|
|
|
|
advantage of them. |
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
That said, L<Web::MREST> itself provides JSON handlers for both the request and |
|
626
|
|
|
|
|
|
|
the response entities, and should be fully UTF-8 clean. Hopefully, this will |
|
627
|
|
|
|
|
|
|
save application developers some work. (For more information, see L<"STATUS |
|
628
|
|
|
|
|
|
|
OBJECTS AND ERROR HANDLING">.) |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
The following subsections detail the principal content negotiation methods. |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
=head3 C<content_types_provided> |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
As the L<Web::Machine::Resource> documentation states, this method must be |
|
635
|
|
|
|
|
|
|
implemented (i.e., by the application) - otherwise, "your resource will not be |
|
636
|
|
|
|
|
|
|
able to return any useful content". |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
Quoting further: "This should return an ARRAY of HASH ref pairs where the key |
|
639
|
|
|
|
|
|
|
is the name of the media type and the value is a CODE ref (or name of a method) |
|
640
|
|
|
|
|
|
|
which can provide a resource representation in that media type." |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
The implementation provided by L<Web::MREST> allows clients to specify (via |
|
643
|
|
|
|
|
|
|
an C<Accept> header) one of two media types: |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=over |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=item C<text/html> |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
Since it is the first hashref pair of the two, it is the default. That means |
|
650
|
|
|
|
|
|
|
if the incoming request does not have an C<Accept> header, the handler |
|
651
|
|
|
|
|
|
|
specified for C<text/html> will be called to generate the response entity. |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
=item C<application/json> |
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
This is the media type that L<Web::MREST> was written to support, both in |
|
656
|
|
|
|
|
|
|
request entities and in response entities. However, there is nothing preventing |
|
657
|
|
|
|
|
|
|
you as the application developer from specifying handlers for other media types. |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=back |
|
660
|
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
If the request includes an C<Accept> header, but none of the media types |
|
662
|
|
|
|
|
|
|
specified in it are found in C<content_types_provided>, L<Web::Machine> will |
|
663
|
|
|
|
|
|
|
generate a C<406 Not Acceptable> response. (Unfortunately, there is no easy way |
|
664
|
|
|
|
|
|
|
for L<Web::MREST> or the application to know in advance that this error will be |
|
665
|
|
|
|
|
|
|
triggered, so it will be returned "bare" - i.e., without any explanatory |
|
666
|
|
|
|
|
|
|
response entity.) |
|
667
|
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
In the normal case when an acceptable handler exists, it will be called to |
|
669
|
|
|
|
|
|
|
generate the response - in other words, whatever is returned by the chosen |
|
670
|
|
|
|
|
|
|
handler becomes the response entity, unless an error occurs inside the handler. |
|
671
|
|
|
|
|
|
|
In that case, the handler should return a reference to a scalar value |
|
672
|
|
|
|
|
|
|
(e.g., \400), which L<Web::Machine> will interpret as an HTTP response code. |
|
673
|
|
|
|
|
|
|
See L<"STATUS OBJECTS AND ERROR HANDLING">. |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
For more on response entity generation, see the sections dedicated to the |
|
676
|
|
|
|
|
|
|
various HTTP methods (L<"GET">, L<"PUT">, L<"POST">, L<"DELETE">), below. |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
=head3 C<content_types_accepted> |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
When the client sends C<PUT> or C<POST> requests, it will typically provide a |
|
682
|
|
|
|
|
|
|
'Content-Type' header specifying the media type of the bytes it is sending in |
|
683
|
|
|
|
|
|
|
the request body. This content type is compared with the media types returned |
|
684
|
|
|
|
|
|
|
by this method. If there is no match, L<Web::Machine> returns a C<415 |
|
685
|
|
|
|
|
|
|
Unsupported Media Type> error response. (Unfortunately, there is no easy way |
|
686
|
|
|
|
|
|
|
for L<Web::MREST> or the application to know in advance that this error will be |
|
687
|
|
|
|
|
|
|
triggered, so it will be returned "bare" - i.e., without any explanatory |
|
688
|
|
|
|
|
|
|
response entity.) |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=head3 Other methods |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
For handling character sets, encodings, and languages, L<Web::Machine> provides |
|
694
|
|
|
|
|
|
|
a number of other content negotiation methods: |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=over |
|
697
|
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
=item C<charsets_provided> |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=item C<default_charset> |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=item C<languages_provided> |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=item C<encodings_provided> |
|
705
|
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=item C<variances> |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=back |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
However, they are only needed if the application does complex content |
|
711
|
|
|
|
|
|
|
negotiation. |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=head2 Part Three (resource existence) |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
When we have made it past content negotiation, we know more than just which |
|
717
|
|
|
|
|
|
|
routines will be used to process the request entity (if any) and generate the |
|
718
|
|
|
|
|
|
|
response. We have gathered quite a bit of information about the request. All |
|
719
|
|
|
|
|
|
|
this information has been pushed onto the context, so it is available to all |
|
720
|
|
|
|
|
|
|
our resource methods, including the resource handler which we will get to |
|
721
|
|
|
|
|
|
|
presently. This information includes: |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
(FIXME: verify this list as it is outdated) |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=over |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=item C<method> |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
The request method |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
=item C<resource_name> |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
The resource name, which can be used as a key to look up the full resource |
|
734
|
|
|
|
|
|
|
definition in the C<< $Web::MREST::InitRouter::resources >> |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=item C<handler_name> |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
The name of the resource handler, e.g. C<handler_bugreport>. In L<Web::MREST>, |
|
739
|
|
|
|
|
|
|
the resource handlers reside in the L<Web::MREST::Dispatch> module. |
|
740
|
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
=item C<uri> |
|
742
|
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
The full URI provided with the request |
|
744
|
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
=item C<uri_base> |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
The base part of the URI (e.g. "http://localhost:5000/" ) |
|
748
|
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=item C<uri_path> |
|
750
|
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
The relative path to the resource (e.g. "/bugreport") |
|
752
|
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
=item C<components> |
|
754
|
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
Reference to an array the elements of which are the individual 'components' |
|
756
|
|
|
|
|
|
|
(i.e., everything between the '/' characters) of the C<uri_path> |
|
757
|
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
=item C<mapping> |
|
759
|
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
A hashref mapping resource parameter names (if any) to their values |
|
761
|
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
=item C<content-length> |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
The content-length header. |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
=item C<content-type> |
|
767
|
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
The content-type header. |
|
769
|
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
=back |
|
771
|
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
One major piece of information is missing, however: whether the resource exists |
|
773
|
|
|
|
|
|
|
or not. For that, we have to actually call the resource handler. |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
=head3 C<resource_exists> (G7) |
|
777
|
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
The term "resource" is not precisely defined. It can refer to the resource |
|
779
|
|
|
|
|
|
|
definition (a data structure), the resource handler (a Perl subroutine called |
|
780
|
|
|
|
|
|
|
as an object method), or an object (set of records) in an underlying database. |
|
781
|
|
|
|
|
|
|
Or it can refer to all of the above, or to something else. The following |
|
782
|
|
|
|
|
|
|
paragraphs describe L<Web::MREST>'s approach. |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
By the time control reaches this method, the request URI has already been |
|
785
|
|
|
|
|
|
|
matched to a resource definition. So the resource handler is known. Since we |
|
786
|
|
|
|
|
|
|
have no other way of knowing, we ask the resource itself, by calling the |
|
787
|
|
|
|
|
|
|
handler with the scalar value C<1> (i.e. the numeral 1) as the sole argument. |
|
788
|
|
|
|
|
|
|
This handler call is referred to as the "first pass". |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
How the handler is implemented does not concern us. We only ask that it return |
|
791
|
|
|
|
|
|
|
a boolean value (true or false) when called with this argument. If the return |
|
792
|
|
|
|
|
|
|
value from the handler is true, we can assume that the handler will be called |
|
793
|
|
|
|
|
|
|
again (second pass) in the response generation phase - read on. |
|
794
|
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
=head2 Part Four (generation of response entity) |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
At this point we have |
|
799
|
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
=over |
|
801
|
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
=item gathered information about the request and placed it on the context |
|
803
|
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
=item run the resource handler (first pass) to determine resource existence |
|
805
|
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
=back |
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
Up until now (i.e., through determination of resource existence), the FSM |
|
809
|
|
|
|
|
|
|
has been a series of steps applied, in the same order, regardless of the |
|
810
|
|
|
|
|
|
|
HTTP method. |
|
811
|
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
In the sections below, we examine how responses are generated for each of |
|
813
|
|
|
|
|
|
|
four HTTP methods (C<GET>, C<PUT>, C<POST>, and C<DELETE>) when the resource |
|
814
|
|
|
|
|
|
|
exists and when it doesn't exist. |
|
815
|
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
=head3 Resource exists |
|
817
|
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
=head4 C<GET> |
|
819
|
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=over |
|
821
|
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
=item 1. C<content_types_provided> method call |
|
823
|
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
First, C<content_types_provided> is called to determine the name of the |
|
825
|
|
|
|
|
|
|
method that is capable of generating the response in the required format. |
|
826
|
|
|
|
|
|
|
This method is the one we mean when we refer to the "response generator". |
|
827
|
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=item 2. Response generator method call |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
Second, the response generator is called (from C<o18> in |
|
831
|
|
|
|
|
|
|
L<Web::Machine::FSM::States>). It is expected to always return an |
|
832
|
|
|
|
|
|
|
L<App::CELL::Status> object. If an error condition is detected, the |
|
833
|
|
|
|
|
|
|
handler should declare it using C<< $self->mrest_declare_status >> |
|
834
|
|
|
|
|
|
|
and then return a "non_ok" status. |
|
835
|
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
=back |
|
837
|
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
C<GET> is the only request method that demands a response entity |
|
839
|
|
|
|
|
|
|
in the format specified by the C<Accept> header. For the other methods, |
|
840
|
|
|
|
|
|
|
response entities are optional, but recommended. In practice, this |
|
841
|
|
|
|
|
|
|
means that we have to create them ourselves. |
|
842
|
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
=head4 C<POST> |
|
845
|
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
Here we have two possible paths, depending on the value returned by |
|
847
|
|
|
|
|
|
|
C<post_is_create>: |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
=over |
|
850
|
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=item C<post_is_create> true |
|
852
|
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
=over |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
=item C<create_path> and C<create_path_after_handler> |
|
856
|
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
If, and only if, C<post_is_create> is true, processing continues via |
|
858
|
|
|
|
|
|
|
C<create_path> and C<create_path_after_handler>. Depending on the value of the |
|
859
|
|
|
|
|
|
|
latter, the request handler (determined by consulting |
|
860
|
|
|
|
|
|
|
C<content_types_accepted>) is called either before or after C<create_path>. |
|
861
|
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
The request handler should stage the response entity in preparation for |
|
863
|
|
|
|
|
|
|
finalization. The content type can be inferred from |
|
864
|
|
|
|
|
|
|
C<< $request->env->{'web.machine.context'} >>. |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
=item Finalization |
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
Request is finalized by a call to C<finish_request>. |
|
869
|
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
=back |
|
871
|
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
=item C<post_is_create> false |
|
873
|
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
If C<post_is_create> returns false, all bets are off. For reasons I do not |
|
875
|
|
|
|
|
|
|
understand, L<Web::Machine> does not consult C<content_types_provided> or |
|
876
|
|
|
|
|
|
|
C<content_types_accepted> on this type of request. The only thing it does is |
|
877
|
|
|
|
|
|
|
call C<process_post>, and so it is up to this method to do whatever needs to be |
|
878
|
|
|
|
|
|
|
done to generate an entity and get it into the response. |
|
879
|
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
L<Web::MREST> helps by making sure that the content type is stored in the |
|
881
|
|
|
|
|
|
|
context (in the C<'content_type'> property), so C<process_post> can look |
|
882
|
|
|
|
|
|
|
there for it and generate the response entity accordingly. |
|
883
|
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
=back |
|
885
|
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
=head4 C<PUT> |
|
888
|
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
On all C<PUT> requests, and those C<POST> requests that are handled as |
|
890
|
|
|
|
|
|
|
C<PUT> requests (see above), L<Web::Machine> uses the following process: |
|
891
|
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
=over |
|
893
|
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
=item C<content_types_accepted> |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
This method is called to determine the name of the method that can process |
|
897
|
|
|
|
|
|
|
the request body. This method is expected not only to process the request |
|
898
|
|
|
|
|
|
|
body, but also to generate the response. Therefore, we refer to this |
|
899
|
|
|
|
|
|
|
method as the "response generator" for C<PUT> requests. |
|
900
|
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
=item Response generator method call |
|
902
|
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
Next, the response generator is called. For C<PUT> requests, the response |
|
904
|
|
|
|
|
|
|
generator is determined from C<content_types_accepted> based on the Here again, the method referred |
|
905
|
|
|
|
|
|
|
to by C<content_types_provided> is not called by L<Web::Machine>, but the |
|
906
|
|
|
|
|
|
|
response generator is free to call C<content_types_provided> and find |
|
907
|
|
|
|
|
|
|
out the method itself, and call it. Or do something else. |
|
908
|
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
When C<resource_exists> is true, the response generator is called from C<o14> |
|
910
|
|
|
|
|
|
|
in L<Web::Machine::FSM::States>. |
|
911
|
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
=back |
|
913
|
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
Whenever a new resource is created, a C<Location> header is added to |
|
915
|
|
|
|
|
|
|
the response with the URI path of the new resource. |
|
916
|
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
In general, we understand C<PUT> to be a request to write to a resource. |
|
918
|
|
|
|
|
|
|
Typically, this will involve either creating (INSERT) or modifying (UPDATE) one |
|
919
|
|
|
|
|
|
|
or more database records/objects. |
|
920
|
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
Therefore, it has to be possible for a URI to resolve to a resource that |
|
922
|
|
|
|
|
|
|
does not yet exist. For example: |
|
923
|
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
PUT employee/nick/Bubba |
|
925
|
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
There may or may not be an employee by the name of Bubba in the database, |
|
927
|
|
|
|
|
|
|
but if we have a resource called 'employee/nick/:nick', Path::Router will |
|
928
|
|
|
|
|
|
|
match it in C<allowed_methods> and the resource handler will be called in |
|
929
|
|
|
|
|
|
|
C<resource_exists> - up until this point, the same sequence of method |
|
930
|
|
|
|
|
|
|
calls is used for C<GET>, C<POST>, C<PUT>, and C<DELETE>. |
|
931
|
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
L<Web::MREST> has no way of knowing whether there is an employee named Bubba. |
|
933
|
|
|
|
|
|
|
It is up to the handler to determine this, and then do an INSERT or UPDATE |
|
934
|
|
|
|
|
|
|
operation as appropriate. This operation is not expected to fail, but if it |
|
935
|
|
|
|
|
|
|
does fail the handler should force a 4xx or 5xx status code (and provide an |
|
936
|
|
|
|
|
|
|
explanation) by calling C<< $self->mrest_declare_status >>. |
|
937
|
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
If the request causes a new object - and, hence, a new resource - to be |
|
939
|
|
|
|
|
|
|
created, the handler should cause a C<Location> header with the URI of the |
|
940
|
|
|
|
|
|
|
new resource to be added to the response. This tells L<Web::Machine> to |
|
941
|
|
|
|
|
|
|
set the response status to C<201 Created>. |
|
942
|
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
If the request only modifies an existing object/resource, simply do not |
|
944
|
|
|
|
|
|
|
add a C<Location> header to the response. This will cause L<Web::Machine> |
|
945
|
|
|
|
|
|
|
to return a C<200 OK> status in the response. |
|
946
|
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
=head4 C<DELETE> |
|
949
|
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
For C<DELETE>, two methods are called: C<delete_resource> and |
|
951
|
|
|
|
|
|
|
C<delete_completed>. The C<delete_resource> method should enact the delete |
|
952
|
|
|
|
|
|
|
operation and generate the response entity. The second method, C<delete_completed>, |
|
953
|
|
|
|
|
|
|
is for cases when the delete operation cannot be guaranteed to have completed - |
|
954
|
|
|
|
|
|
|
this method defaults to false, but if it returns true L<Web::Machine> will |
|
955
|
|
|
|
|
|
|
trigger a C<...> response. |
|
956
|
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=head3 Resource does not exist |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
=head4 C<GET> |
|
961
|
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
Request goes to finalization with 404 status. |
|
963
|
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
=head4 C<POST> |
|
965
|
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
Request goes to C<allow_missing_post>, which always returns false in |
|
967
|
|
|
|
|
|
|
L<Web::MREST>'s implementation. |
|
968
|
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
After that, the request goes to finalization with 404 status. |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
If the |
|
972
|
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
=head4 C<PUT> |
|
974
|
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
=head2 C<finish_request> |
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
The previous sections should suffice for the reader to gain a degree of |
|
980
|
|
|
|
|
|
|
understanding of how the state machine works for various types of requests, and |
|
981
|
|
|
|
|
|
|
how L<Web::MREST> interfaces with the response handlers. |
|
982
|
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
The last cog of the FSM is C<finish_request>. |
|
984
|
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
=head1 IN-DEPTH DISCUSSIONS OF VARIOUS TOPICS |
|
988
|
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
=head2 Resource definitions |
|
990
|
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
As we read in the "crash course" above, resources are central to what a REST |
|
992
|
|
|
|
|
|
|
server is and does: the server processes incoming requests. Each request has |
|
993
|
|
|
|
|
|
|
a URI which resolves (or does not resolve) to a resource. Resources are |
|
994
|
|
|
|
|
|
|
defined as module variables: each module that contains resource handlers |
|
995
|
|
|
|
|
|
|
should also define a module variable (via C<our $resource_defs = { ... };>) |
|
996
|
|
|
|
|
|
|
containing the definitions of the resources covered by that module. |
|
997
|
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
The top-level dispatch module, L<Web::MREST::Dispatch>, should implement |
|
999
|
|
|
|
|
|
|
a method called C<init_router> which calls the function |
|
1000
|
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
Web::MREST::InitRouter::load_resource_defs |
|
1002
|
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
for all the resource-defining modules. When the first HTTP request comes in, |
|
1004
|
|
|
|
|
|
|
L<Web::MREST::Resource> calls the C<init_router> method. This only happens |
|
1005
|
|
|
|
|
|
|
once, ensuring that the resource definitions are fully loaded for the first - |
|
1006
|
|
|
|
|
|
|
and all subsequent - requests. |
|
1007
|
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
Each resource definition is a hashref consisting of a number of properties. |
|
1009
|
|
|
|
|
|
|
This definition hashref is itself included in the C<$resources> package |
|
1010
|
|
|
|
|
|
|
hashref, which essentially looks like this: |
|
1011
|
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
{ |
|
1013
|
|
|
|
|
|
|
RESOURCE_NAME => RESOURCE_DEFINTION, |
|
1014
|
|
|
|
|
|
|
RESOURCE_NAME => RESOURCE_DEFINTION, |
|
1015
|
|
|
|
|
|
|
RESOURCE_NAME => RESOURCE_DEFINTION, |
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
where C<RESOURCE_NAME> is a resource name (a string like C<'/'> or |
|
1019
|
|
|
|
|
|
|
C<'docu/text'>) and C<RESOURCE_DEFINITION> is that resource's definition |
|
1020
|
|
|
|
|
|
|
hashref. |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
The root resource should be defined under the name C<'/'> and top-level |
|
1023
|
|
|
|
|
|
|
resources should have a C<parent> property set to this string. |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
In the resource definition, properties can be specified either as a |
|
1026
|
|
|
|
|
|
|
scalar value, in which case the definition applies to all the methods |
|
1027
|
|
|
|
|
|
|
specified in C<< $site->MREST_SUPPORTED_HTTP_METHODS >>, or as a |
|
1028
|
|
|
|
|
|
|
hashref in case the given resource is only defined for certain methods. |
|
1029
|
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
In the latter case, it is not necessary to define all properties as |
|
1031
|
|
|
|
|
|
|
hashrefs. The set of permitted methods will always be taken from the |
|
1032
|
|
|
|
|
|
|
'handler' property. For example in this snippet whizzo_resource is only |
|
1033
|
|
|
|
|
|
|
defined for the GET method, and that will be applied to 'foo' (and the |
|
1034
|
|
|
|
|
|
|
rest of this resource's properties) as well. |
|
1035
|
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
'whizzo_resource' => { |
|
1037
|
|
|
|
|
|
|
'handler' => { |
|
1038
|
|
|
|
|
|
|
'GET' => 'some_method', |
|
1039
|
|
|
|
|
|
|
}, |
|
1040
|
|
|
|
|
|
|
'foo' => 'barbazbat', |
|
1041
|
|
|
|
|
|
|
... |
|
1042
|
|
|
|
|
|
|
} |
|
1043
|
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
So 'foo' will only be defined for the GET method. |
|
1045
|
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
Examples: |
|
1047
|
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
'foo_prop' => 'value applied to all available methods', |
|
1049
|
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
'bar_prop' => { |
|
1051
|
|
|
|
|
|
|
'GET' => 'value applied to GET requests', |
|
1052
|
|
|
|
|
|
|
'POST' => 'value applied to POST requests', |
|
1053
|
|
|
|
|
|
|
}, |
|
1054
|
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
There is one required property, 'handler', which is used to specify the |
|
1056
|
|
|
|
|
|
|
handler(s) for the resource (see the examples below). The value of this |
|
1057
|
|
|
|
|
|
|
property is taken to be the name of a method. This method call looks |
|
1058
|
|
|
|
|
|
|
like this: |
|
1059
|
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
$self->$handler |
|
1061
|
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
and is located in Web::MREST::Resource->resource_exists |
|
1063
|
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
(The inheritance chain is set up in C<bin/mrest> - the server startup script - |
|
1065
|
|
|
|
|
|
|
and via C<use parent> statements in the various modules that make up the |
|
1066
|
|
|
|
|
|
|
inheritance chain.) |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
In addition, each resource may have any properties you, the application |
|
1069
|
|
|
|
|
|
|
developer, wish to invest in it. For our 'docu' methods we use the |
|
1070
|
|
|
|
|
|
|
properties 'description' and 'documentation', for example. |
|
1071
|
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
Two properties - 'parent' and 'validations' - are |
|
1073
|
|
|
|
|
|
|
exceptions to the above and should never be defined on a per-method |
|
1074
|
|
|
|
|
|
|
basis: |
|
1075
|
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
- 'validations' contains validation checks to be applied when matching |
|
1077
|
|
|
|
|
|
|
URI to resource (for more information, see the Path::Router |
|
1078
|
|
|
|
|
|
|
documentation). |
|
1079
|
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
- 'parent' contains the name of the resource's parent resource |
|
1081
|
|
|
|
|
|
|
(defaults to '' - the root resource) |
|
1082
|
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
- 'documentation' is reserved for the self-documentation feature |
|
1084
|
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
=head3 C<Path::Router> object initialization |
|
1088
|
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
When the server starts, the C<MREST_RESOURCE_DEFINITIONS> and |
|
1090
|
|
|
|
|
|
|
C<MREST_ROOT_RESOURCE> meta parameters are initialized from the configuration |
|
1091
|
|
|
|
|
|
|
file C<config/dispatch_MetaConfig.pm> in the L<Web::MREST> distribution. |
|
1092
|
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
The application developer will of course want to define her own set of |
|
1094
|
|
|
|
|
|
|
resources. This should be done by manipulating the meta parameters |
|
1095
|
|
|
|
|
|
|
C<MREST_RESOURCE_DEFINITIONS> and C<MREST_ROOT_RESOURCE>. A good place |
|
1096
|
|
|
|
|
|
|
to do this is in the application's C<mrest_init_router> routine. |
|
1097
|
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
Here are two approaches to defining the application's resources, depending on |
|
1099
|
|
|
|
|
|
|
whether the application wishes to retain the L<Web::MREST> resources. |
|
1100
|
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
=over |
|
1102
|
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
=item 1. retain |
|
1104
|
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
package MyApp::Resource; |
|
1106
|
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
use Clone 'clone'; |
|
1108
|
|
|
|
|
|
|
use parent 'Web::MREST::Resource'; |
|
1109
|
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
# We assume that the application somehow loads its resource definitions |
|
1111
|
|
|
|
|
|
|
# (including the root resource) into a package variable $r_defs -- for |
|
1112
|
|
|
|
|
|
|
# example by hard-coding them like this |
|
1113
|
|
|
|
|
|
|
my $r_defs = { ... }; |
|
1114
|
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
# ---------------------------------------- |
|
1116
|
|
|
|
|
|
|
# mrest_init_router - called by Web::MREST |
|
1117
|
|
|
|
|
|
|
# ---------------------------------------- |
|
1118
|
|
|
|
|
|
|
sub mrest_init_router { |
|
1119
|
|
|
|
|
|
|
my $self = shift; |
|
1120
|
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
# set up the root resource |
|
1122
|
|
|
|
|
|
|
$meta->set( 'MREST_ROOT_RESOURCE', $r_defs->{''} ); |
|
1123
|
|
|
|
|
|
|
delete $r_defs->{''}; |
|
1124
|
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
# set up the remaining resources, retaining (but possibly |
|
1126
|
|
|
|
|
|
|
# overwriting) the Web::MREST default resources |
|
1127
|
|
|
|
|
|
|
my $mrest_defs = clone( $meta->MREST_RESOURCE_DEFINITIONS ); |
|
1128
|
|
|
|
|
|
|
foreach my $r_name ( keys %$r_defs ) { |
|
1129
|
|
|
|
|
|
|
$mrest_defs->{$r_name} = $r_defs->{$r_name}; |
|
1130
|
|
|
|
|
|
|
} |
|
1131
|
|
|
|
|
|
|
$meta->set( 'MREST_RESOURCE_DEFINITIONS', $mrest_defs ); |
|
1132
|
|
|
|
|
|
|
} |
|
1133
|
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
=item 2. do not retain |
|
1135
|
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
This approach is more simple because no C<mrest_init_router> need be written. |
|
1137
|
|
|
|
|
|
|
The application should have its own distro sharedir C<config/> and therein a |
|
1138
|
|
|
|
|
|
|
file C<dispatch_MetaConfig.pm>. Inside that file, the application puts its own |
|
1139
|
|
|
|
|
|
|
resource definitions in the C<MREST_RESOURCE_DEFINITIONS> and |
|
1140
|
|
|
|
|
|
|
C<MREST_ROOT_RESOURCE> parameters (refer to C<config/dispatch_MetaConfig.pm> in |
|
1141
|
|
|
|
|
|
|
the L<Web::MREST> distribution for syntax and semantics). |
|
1142
|
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
The application's definitions will overlay (i.e. replace) those of |
|
1144
|
|
|
|
|
|
|
L<Web::MREST>. Even in this scenario, some or all of L<Web::MREST>'s resources |
|
1145
|
|
|
|
|
|
|
could be used in the application, but only by copy-pasting the definitions and |
|
1146
|
|
|
|
|
|
|
their respective handlers into the application's source code. |
|
1147
|
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
=back |
|
1149
|
|
|
|
|
|
|
|
|
1150
|
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
=head3 Tree structure |
|
1152
|
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
L<Web::MFILE> allows resources to be defined in a tree structure. It is |
|
1154
|
|
|
|
|
|
|
designed to allow a tree structure to be described in a flat configuration |
|
1155
|
|
|
|
|
|
|
file. The C<MREST_RESOURCE_DEFINITIONS> hash is keyed on the resource name. |
|
1156
|
|
|
|
|
|
|
Child resources are indicated by including a C<parent> property with the name |
|
1157
|
|
|
|
|
|
|
of the parent resource. Care should be exercised not to introduce any circular |
|
1158
|
|
|
|
|
|
|
references. |
|
1159
|
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
If a flat structure is desired, simply do not include any C<parent> properties |
|
1161
|
|
|
|
|
|
|
in your resource definitions. |
|
1162
|
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
The format of C<MREST_RESOURCE_DEFINITIONS> hash is documented in |
|
1164
|
|
|
|
|
|
|
C<config/dispatch_MetaConfig.pm>. |
|
1165
|
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
=head3 C<< $Web::MREST::InitRouter::resources >> |
|
1168
|
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
The resource definition hashrefs in the dispatch modules are designed to be |
|
1170
|
|
|
|
|
|
|
written and maintained by humans. When the C<init_router> method runs, it loops |
|
1171
|
|
|
|
|
|
|
over all the resource definitions and builds up a second hash, |
|
1172
|
|
|
|
|
|
|
C<< $Web::MREST::InitRouter::resources >>, which contains the same information |
|
1173
|
|
|
|
|
|
|
in a format that is more convenient for automated processing. |
|
1174
|
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
Since the resource definitions are a potential source of typographical and |
|
1176
|
|
|
|
|
|
|
semantic errors, you should dump this package variable to the log and examine |
|
1177
|
|
|
|
|
|
|
it to make sure your resource definitions are being processed correctly. |
|
1178
|
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
=head2 Errors |
|
1181
|
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
As we move through the state machine (i.e. the chain of method calls driven |
|
1183
|
|
|
|
|
|
|
by L<Web::Machine>), we build up a "context" from which we generate the HTTP |
|
1184
|
|
|
|
|
|
|
response. Stated very simply, the response code can either be 'OK' (200) or |
|
1185
|
|
|
|
|
|
|
"something else" - i.e., an error of some kind. |
|
1186
|
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
And, indeed, checking for errors accounts for a large portion of what our |
|
1188
|
|
|
|
|
|
|
resource modules do. As RFC2616 explains, errors can be divided into two |
|
1189
|
|
|
|
|
|
|
brought classes: client errors and server errors. |
|
1190
|
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
=over |
|
1192
|
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
=item Client errors (4xx) |
|
1194
|
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
Client errors have status codes that start with 4 (e.g. 400, 401, 404). |
|
1196
|
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
RFC2616 has this to say about them: |
|
1198
|
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
The 4xx class of status code is intended for cases in which the client |
|
1200
|
|
|
|
|
|
|
seems to have erred. Except when responding to a HEAD request, the server |
|
1201
|
|
|
|
|
|
|
SHOULD include an entity containing an explanation of the error situation, and |
|
1202
|
|
|
|
|
|
|
whether it is a temporary or permanent condition. These status codes are |
|
1203
|
|
|
|
|
|
|
applicable to any request method. User agents SHOULD display any included |
|
1204
|
|
|
|
|
|
|
entity to the user. |
|
1205
|
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
=item Server errors (5xx) |
|
1207
|
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
Server errors have codes beginning with th digit "5". According to RFC2616, |
|
1209
|
|
|
|
|
|
|
they |
|
1210
|
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
indicate cases in which the server is aware that it has erred or is |
|
1212
|
|
|
|
|
|
|
incapable of performing the request. Except when responding to a HEAD |
|
1213
|
|
|
|
|
|
|
request, the server SHOULD include an entity containing an explanation of |
|
1214
|
|
|
|
|
|
|
the error situation, and whether it is a temporary or permanent condition. |
|
1215
|
|
|
|
|
|
|
User agents SHOULD display any included entity to the user. These response |
|
1216
|
|
|
|
|
|
|
codes are applicable to any request method. |
|
1217
|
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
=back |
|
1219
|
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
The key point here is that it is not sufficient to return a bare 4xx or 5xx |
|
1221
|
|
|
|
|
|
|
response status code. The response should include an entity body with an |
|
1222
|
|
|
|
|
|
|
explanation of the error condition. |
|
1223
|
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
=head3 How to provide explanation in response entity |
|
1225
|
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
L<Web::MREST> provides a mechanism for adding the explanation to the entity |
|
1227
|
|
|
|
|
|
|
body as called for by RFC2616. At the exact place in your resource module |
|
1228
|
|
|
|
|
|
|
where you discover the error, do something like this: |
|
1229
|
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
$self->mrest_declare_status( code => '400', explanation => 'You messed up' ); |
|
1231
|
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
This will be converted into the respective L<App::CELL::Status> object and |
|
1233
|
|
|
|
|
|
|
returned in the response entity. The object will have properties like this: |
|
1234
|
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
{ |
|
1236
|
|
|
|
|
|
|
level => 'ERR', |
|
1237
|
|
|
|
|
|
|
code => 'You messed up', |
|
1238
|
|
|
|
|
|
|
payload => { |
|
1239
|
|
|
|
|
|
|
http_code => '500', |
|
1240
|
|
|
|
|
|
|
uri_path => ... (taken from the context), |
|
1241
|
|
|
|
|
|
|
resource_name => ... (taken from the context), |
|
1242
|
|
|
|
|
|
|
found_in => ... (taken from 'caller'), |
|
1243
|
|
|
|
|
|
|
permanent => JSON::true (the default), |
|
1244
|
|
|
|
|
|
|
}, |
|
1245
|
|
|
|
|
|
|
} |
|
1246
|
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
Alternatively, you can pass in your own arbitary L<App::CELL::Status> object. |
|
1248
|
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
To see how the L<App::CELL::Status> object becomes the response entity, see |
|
1250
|
|
|
|
|
|
|
the C<finish_request> method in L<Web::MREST::Resource>. |
|
1251
|
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
=head2 Context |
|
1254
|
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
Typically referred to as C<$context>, the "MREST context" is a hashref that is |
|
1256
|
|
|
|
|
|
|
built up during the course of request processing. In addition to being used |
|
1257
|
|
|
|
|
|
|
within L<Web::MREST::Resource>, it is always sent as an argument whenever |
|
1258
|
|
|
|
|
|
|
L<Web::MREST::Resource> calls a hook, so the developer can modify it in her |
|
1259
|
|
|
|
|
|
|
implementations of the various hook routines. |
|
1260
|
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
=head2 Authentication |
|
1263
|
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
Ever since the Big Bad Wolf ate Granny, authentication mechanisms have been |
|
1265
|
|
|
|
|
|
|
prone to abuse by individuals who are willing to lie about their identity. |
|
1266
|
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
Humans are good at distinguishing one human from another, provided they can |
|
1268
|
|
|
|
|
|
|
apply all their senses to the task. Computers lack proper senses and are |
|
1269
|
|
|
|
|
|
|
downright awful at this task. Computerized authentication schemes typically |
|
1270
|
|
|
|
|
|
|
operate by presenting the user with one or more hoops to jump through. Whoever |
|
1271
|
|
|
|
|
|
|
succeeds at this task is deemed to be the user. What could go wrong? |
|
1272
|
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
Passwords (or passphrases) are the "hoop" most frequently used to authenticate |
|
1274
|
|
|
|
|
|
|
users and keep would-be intruders out. Therefore, a system's security is often |
|
1275
|
|
|
|
|
|
|
gauged by how well it protects user credentials from disclosure. Since |
|
1276
|
|
|
|
|
|
|
usernames are public, the only thing keeping a determined intruder at bay are |
|
1277
|
|
|
|
|
|
|
the passwords, and various measures are taken to protect them. |
|
1278
|
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
From the perspective of L<Web::Machine>, authentication is a matter of |
|
1280
|
|
|
|
|
|
|
calling the L<is_authorized> method. If the return value is false, the response |
|
1281
|
|
|
|
|
|
|
will be C<401 Unauthorized>. If it is true, request processing continues. |
|
1282
|
|
|
|
|
|
|
Whatever authentication measures the application developer decides to implement |
|
1283
|
|
|
|
|
|
|
should be triggered by this method call. |
|
1284
|
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
For more about L<is_authorized>, see the L<Web::Machine::Resource documentation|https://metacpan.org/pod/Web::Machine::Resource#is_authorized-authorization_header> |
|
1286
|
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
=head2 Authorization |
|
1290
|
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
Once authentication has determined the user's identity, a related task, |
|
1292
|
|
|
|
|
|
|
authorization, begins. As the name would imply (and the RFC's vague |
|
1293
|
|
|
|
|
|
|
use of the term "authorization" notwithstanding), authorization answers |
|
1294
|
|
|
|
|
|
|
the question: |
|
1295
|
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
Is this specific user authorized to make this request? |
|
1297
|
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
Compare this with authentication, which answers a different question: |
|
1299
|
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
Is this user really who they are purporting to be? |
|
1301
|
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
Or, even more pithily: |
|
1303
|
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
Who is this user? |
|
1305
|
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
Authorization implies a boolean "function" (in both the mathematical and |
|
1307
|
|
|
|
|
|
|
computer science sense) that takes three arguments: the username, the HTTP |
|
1308
|
|
|
|
|
|
|
method, and the resource. Implementation of this function is left to the |
|
1309
|
|
|
|
|
|
|
application developer. |
|
1310
|
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
It is worth noting here that L<Web::Machine> provides a C<forbidden> method. |
|
1312
|
|
|
|
|
|
|
Since C<is_authorized> is already taken for authentication, we can use |
|
1313
|
|
|
|
|
|
|
C<forbidden> for authorization. Just be sure to understand thoroughly that |
|
1314
|
|
|
|
|
|
|
a true return value from C<forbidden> means "not authorized". |
|
1315
|
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
=head2 Customized URI parsing |
|
1318
|
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
While L<Web::MREST> provides for URI parsing using L<Path::Router>, if this is |
|
1320
|
|
|
|
|
|
|
not desired the application developer can parse URIs herself by simply |
|
1321
|
|
|
|
|
|
|
substituting her own C<init_router> and C<match> methods for the ones provided |
|
1322
|
|
|
|
|
|
|
by L<Path::Router> and L<Path::Router::Route::Match>, respectively. |
|
1323
|
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
When request processing enters C<resource_exists>, |
|
1325
|
|
|
|
|
|
|
Alternatively, the application developer can overlay the C<init_router> routine |
|
1326
|
|
|
|
|
|
|
with one that returns an arbitrary object (stored in C<$router>) that has a |
|
1327
|
|
|
|
|
|
|
C<match> method. After that, L<Web::MREST> does |
|
1328
|
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
my $match = $router->match( $path ); |
|
1330
|
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
where C<$path> is the relative portion of the URI (i.e. everything left after |
|
1332
|
|
|
|
|
|
|
the C<http://myapp.example.com/> part is cut off). |
|
1333
|
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
The C<$match> object should provide a C<route> method, which should return the |
|
1335
|
|
|
|
|
|
|
definition of the matched resource. See L<"RESOURCE DEFINITIONS">. |
|
1336
|
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
=head1 FUNCTIONS IN THIS MODULE |
|
1339
|
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
=head2 init |
|
1341
|
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
Do initialization-like things, such as loading configuration parameters. |
|
1343
|
|
|
|
|
|
|
Takes a PARAMHASH which can contain one of the following: |
|
1344
|
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
=over |
|
1346
|
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
=item C<distro> |
|
1348
|
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
The name of the application distribution from which the distro sharedir will be |
|
1350
|
|
|
|
|
|
|
loaded. |
|
1351
|
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
=item C<path> |
|
1353
|
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
The name (full path) of a directory containing the application's configuration |
|
1355
|
|
|
|
|
|
|
files. |
|
1356
|
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
=item C<hashref> |
|
1358
|
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
A reference to a hash containing meta parameters to be loaded. |
|
1360
|
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
=back |
|
1362
|
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
=cut |
|
1364
|
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
sub init { |
|
1366
|
22
|
|
|
22
|
1
|
1186
|
my %ARGS = validate( @_, { |
|
1367
|
|
|
|
|
|
|
distro => { type => SCALAR, optional => 1 }, |
|
1368
|
|
|
|
|
|
|
sitedir => { type => SCALAR, optional => 1 }, |
|
1369
|
|
|
|
|
|
|
hashref => { type => HASHREF, optional => 1 }, |
|
1370
|
|
|
|
|
|
|
early_debug => { type => SCALAR, optional => 1 }, |
|
1371
|
|
|
|
|
|
|
} ); |
|
1372
|
|
|
|
|
|
|
|
|
1373
|
22
|
|
|
|
|
136
|
my $tf = $ARGS{'early_debug'}; |
|
1374
|
22
|
50
|
|
|
|
95
|
if ( $tf ) { |
|
1375
|
0
|
|
|
|
|
0
|
_touch $tf; |
|
1376
|
0
|
0
|
0
|
|
|
0
|
if ( -r $tf and -w $tf ) { |
|
1377
|
0
|
|
|
|
|
0
|
unlink $tf; |
|
1378
|
0
|
|
|
|
|
0
|
Log::Any::Adapter->set( 'File', $tf ); |
|
1379
|
0
|
|
|
|
|
0
|
$log->debug( __PACKAGE__ . "::init activating early debug logging to $tf" ); |
|
1380
|
|
|
|
|
|
|
} else { |
|
1381
|
0
|
|
|
|
|
0
|
print "Given unreadable/unwritable early debugging filespec $tf\n"; |
|
1382
|
|
|
|
|
|
|
} |
|
1383
|
|
|
|
|
|
|
} |
|
1384
|
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
# always load Web::MREST's configuration parameters |
|
1386
|
22
|
|
|
|
|
132
|
my $target = File::ShareDir::dist_dir('Web-MREST'); |
|
1387
|
22
|
|
|
|
|
2182
|
$log->debug( "About to load Web::MREST configuration parameters from $target" ); |
|
1388
|
22
|
|
|
|
|
4079
|
my $status = $CELL->load( sitedir => $target, verbose => 1 ); |
|
1389
|
22
|
50
|
|
|
|
92639
|
return $status if $status->not_ok; |
|
1390
|
|
|
|
|
|
|
|
|
1391
|
22
|
|
|
|
|
438
|
$meta->set( 'MREST_EARLY_DEBUGGING', $tf ); |
|
1392
|
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
# if argument provided, load that, too |
|
1394
|
22
|
50
|
|
|
|
1581
|
if ( %ARGS ) { |
|
1395
|
0
|
|
|
|
|
0
|
$target = undef; |
|
1396
|
0
|
0
|
0
|
|
|
0
|
if ( $ARGS{'distro'} and $ARGS{'distro'} ne 'Web-MREST' ) { |
|
1397
|
|
|
|
|
|
|
# distro must be given as "MyApp-Foo", not "MyApp::Foo" |
|
1398
|
0
|
|
|
|
|
0
|
$target = File::ShareDir::dist_dir( $ARGS{'distro'} ); |
|
1399
|
0
|
|
|
|
|
0
|
$status = $CELL->load( sitedir => $target ); |
|
1400
|
0
|
0
|
|
|
|
0
|
return $status if $status->not_ok; |
|
1401
|
|
|
|
|
|
|
} |
|
1402
|
0
|
0
|
|
|
|
0
|
if ( my $sitedir_target = $ARGS{'sitedir'} ) { |
|
1403
|
0
|
0
|
|
|
|
0
|
if ( -d $sitedir_target ) { |
|
1404
|
0
|
|
|
|
|
0
|
$status = $CELL->load( sitedir => $sitedir_target ); |
|
1405
|
0
|
0
|
|
|
|
0
|
return $status if $status->not_ok; |
|
1406
|
|
|
|
|
|
|
} else { |
|
1407
|
0
|
|
|
|
|
0
|
$log->warn( 'Web::MREST::init() says sitedir argument given, but it is not a directory: ' . |
|
1408
|
|
|
|
|
|
|
Dumper( $sitedir_target ) ); |
|
1409
|
|
|
|
|
|
|
} |
|
1410
|
|
|
|
|
|
|
} |
|
1411
|
0
|
0
|
|
|
|
0
|
if ( $ARGS{'hashref'} ) { |
|
1412
|
0
|
|
|
|
|
0
|
my $count = 0; |
|
1413
|
0
|
|
|
|
|
0
|
foreach my $key ( keys %{ $ARGS{'hashref'} } ) { |
|
|
0
|
|
|
|
|
0
|
|
|
1414
|
0
|
|
|
|
|
0
|
$meta->set( $key, $ARGS{'hashref'}->{$key} ); |
|
1415
|
0
|
|
|
|
|
0
|
$count += 1; |
|
1416
|
|
|
|
|
|
|
} |
|
1417
|
0
|
|
|
|
|
0
|
$log->notice( "Web::MREST::init loaded $count meta parameters from a hashref" ); |
|
1418
|
|
|
|
|
|
|
} |
|
1419
|
|
|
|
|
|
|
} |
|
1420
|
|
|
|
|
|
|
|
|
1421
|
22
|
|
|
|
|
135
|
return $CELL->status_ok; |
|
1422
|
|
|
|
|
|
|
} |
|
1423
|
|
|
|
|
|
|
|
|
1424
|
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
=head2 version |
|
1426
|
|
|
|
|
|
|
|
|
1427
|
|
|
|
|
|
|
Accessor method (to be called like a constructor) providing access to C<$VERSION> variable |
|
1428
|
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
=cut |
|
1430
|
|
|
|
|
|
|
|
|
1431
|
2
|
|
|
2
|
1
|
594
|
sub version { $VERSION; } |
|
1432
|
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
1; |