-
Notifications
You must be signed in to change notification settings - Fork 28
Resource Functions
All Webmachine resources should include the Webmachine resource library:
-include_lib("webmachine/include/webmachine.hrl").
All Webmachine resources should define and export =init/1=, which will
receive a configuration property list from the dispatcher as its argument
for every request to that resource. This function should, if successful, return
{ok, Context}
. Context
is any term, and will be threaded through
all of the other Webmachine resource functions. Alternately, the resource
can go into debugging mode by returning {{trace, Dir}, Context}
-- see the
Debugging documentation for more information.
All Webmachine resource functions are of the signature:
f(ReqData, Context) -> {Result, ReqData, Context}
Context
is an arbitrary term()
that is specific to your
application. Webmachine will never do anything with this term other
than threading it through the various functions of your resource. This
is the means by which transient application-specific request state is
passed along between functions.
ReqData
is a #wm_reqdata{}
term, and is manipulated via the wrq
interface. A resource function may access request data (such as header
values) from the input value. If a resource function wishes to affect
the response data in some way other than that implied by its return
value (e.g. adding an X-Header
) then it should modify the returned
ReqData
term accordingly.
The rest of this document is about the effects produced by different
values in the Result
term from different resource functions.
In the function list below if a function has a result type including
halt()
, it also has the option of returning either of the two
following special values for Result
:
Result | Effect |
---|---|
{error,Err::term()} |
Immediately end processing of this request, returning a
500 Internal Server Error response. The response body
will contain the Err term. |
{halt,200..599} |
Immediately end processing of this request, returning response
code Code . It is the responsibility of the resource to
ensure that all necessary response header and body elements are filled
in ReqData in order to make that response code
valid. |
There are over 30 resource functions you can define, but any of them
can be omitted as they have reasonable defaults. Each function is
described below, showing the default and allowed values that may be in
the Result
term. The default will be used if a resource does not
export the function.
Any function which has no description is optional and the effect of its return value should be evident from examining the Diagram. The following types are used in the descriptions below:
%% The Request data record
-type rd() :: wrq:reqdata().
%% Used in callbacks that can halt or error. See "Halting Resources" above.
-type halt() :: {error, term()} | {halt, 200..599}.
%% The resource's internal state
-type state() :: term().
%% The body given by streaming responses.
-type streambody() :: {iodata(), fun(() -> streambody()) | done}.
The below are all of the supported predefined resource functions. In
addition to whichever of these a resource wishes to use, it also must
export all of the functions named in the return values of the
content_types_provided
and content_types_accepted
functions, with
behavior as described in the last two resource functions below.
-spec resource_exists(rd(), state()) -> {boolean() | halt(), rd(), state()}.
Returning non-true values will result in 404 Not Found
.
Default : true
-spec service_available(rd(), state()) -> {boolean() | halt(), rd(), state()}.
Returning non-true values will result in 503 Service Unavailable
.
Default : true
-spec is_authorized(rd(), state()) -> {true | string() | halt(), rd(), state()}.
If this returns anything other than true
, the response will be 401 Unauthorized
. The string()
return value will be used as the value
in the WWW-Authenticate
header, for example Basic realm="Webmachine"
.
Default : true
-spec forbidden(rd(), state()) -> {boolean() | halt(), rd(), state()}.
Returning true will result in 403 Forbidden
.
Default : false
-spec allow_missing_post(rd(), state()) -> {boolean() | halt(), rd(), state()}.
If the resource accepts POST requests to nonexistent resources, then
this should return true
.
Default : false
-spec malformed_request(rd(), state()) -> {boolean() | halt(), rd(), state()}.
Returning true will result in 400 Bad Request
.
Default : false
-spec uri_too_long(rd(), state()) -> {boolean() | halt(), rd(), state()}.
Returning true will result in 414 Request-URI Too Long
.
Default : false
-spec known_content_type(rd(), state()) -> {boolean() | halt(), rd(), state()}.
Returning false will result in 415 Unsupported Media Type
.
Default : true
-spec valid_content_headers(rd(), state()) -> {boolean() | halt(), rd(), state()}.
Returning false will result in 501 Not Implemented
.
Default : true
-spec valid_entity_length(rd(), state()) -> {boolean() | halt(), rd(), state()}.
Returning false will result in 413 Request Entity Too Large
.
Default : false
-spec options(rd(), state()) -> {[{string(), string()}], rd(), state()}.
If the OPTIONS method is supported and is used, the return value of this function is expected to be a list of pairs representing header names and values that should appear in the response.
-spec allowed_methods(rd(), state()) -> {[Method], rd(), state()}
when Method :: 'GET' | 'HEAD' | 'PUT' | 'POST' |
'DELETE' | 'OPTIONS'.
If a Method
not in this list is requested, then a 405 Method Not Allowed
will be sent. Note that these are all-caps and are atoms.
(single-quoted)
Default : ['GET', 'HEAD']
-spec delete_resource(rd(), state()) -> {boolean() | halt(), rd(), state()}.
This is called when a DELETE request should be enacted, and should
return true
if the deletion succeeded.
-spec delete_resource(rd(), state()) -> {boolean() | halt(), rd(), state()}.
This is only called after a successful delete_resource
call, and
should return false
if the deletion was accepted but cannot yet be
guaranteed to have finished.
-spec post_is_create(rd(), state()) -> {boolean(), rd(), state()}.
If POST requests should be treated as a request to put content into a
(potentially new) resource as opposed to being a generic submission
for processing, then this function should return true
. If it does
return true
, then create_path
will be called and the rest of the
request will be treated much like a PUT to the Path
entry returned
by that call.
Default : false
-spec create_path(rd(), state()) -> {Path::string(), rd(), state()}.
This will be called on a POST request if post_is_create
returns
true
. It is an error for this function not to produce a Path
if
post_is_create
returns true
. The Path
returned should be a valid
URI part following the dispatcher prefix. That Path
will replace the
previous one in the return value of wrq:disp_path(ReqData)
for all
subsequent resource function calls in the course of this request.
-spec process_post(rd(), state()) -> {boolean() | halt(), rd(), state()}.
If post_is_create
returns false
, then this will be called to
process any POST requests. If it succeeds, it should return true
.
-spec content_types_provided(rd(), state()) -> {[{MediaType::string(), Handler::atom()}], rd(), state()}.
This should return a list of pairs where each pair is of the form
{MediaType, Handler}
where MediaType
is a string of content-type
format and the Handler
is an atom naming the function which can
provide a resource representation in that media type. Content
negotiation is driven by this return value. For example, if a client
request includes an Accept
header with a value that does not appear
as a first element in any of the return tuples, then a 406 Not Acceptable
will be sent.
Default : [{"text/html", to_html}]
-spec content_types_accepted(rd(), state()) -> {[{MediaType::string(), Handler::atom()}], rd(), state()}.
This is used similarly to content_types_provided
, except that it is
for incoming resource representations -- for example, PUT requests.
Handler functions usually want to use wrq:req_body(ReqData)
to
access the incoming request body.
Default : []
-spec content_types_accepted(rd(), state()) -> {no_charset | [{Charset::string(), Converter}], rd(), state()}
when Converter :: fun((iodata()) -> iodata()).
If this is anything other than the atom no_charset
, it must be a
list of pairs where each pair is of the form {Charset, Converter}
where Charset
is a string naming a charset and Converter
is a
callable function in the resource which will be called on the produced
body in a GET and ensure that it is in Charset
.
Default : no_charset
-spec encodings_provided(rd(), state()) -> {[{Encoding::string(), Encoder}], rd(), state()}
when Encoder :: fun((iodata()) -> iodata()).
This must be a list of pairs where in each pair Encoding
is a string
naming a valid content encoding and Encoder
is a callable function
in the resource which will be called on the produced body in a GET and
ensure that it is so encoded. One useful setting is to have the
function check on method, and on GET requests return:
[{"identity", fun(X) -> X end},
{"gzip", fun(X) -> zlib:gzip(X) end}]
as this is all that is needed to support gzip content encoding.
Default : [{"identity", fun(X) -> X end}]
-spec variances(rd(), state()) -> {[HeaderName::string()], rd(), state()}.
If this function is implemented, it should return a list of strings
with header names that should be included in a given response's Vary
header. The standard conneg headers (Accept
, Accept-Encoding
,
Accept-Charset
, Accept-Language
) do not need to be specified here
as Webmachine will add the correct elements of those automatically
depending on resource behavior.
Default : []
-spec is_conflict(rd(), state()) -> {boolean(), rd(), state()}.
If this returns true
, the client will receive a 409 Conflict
.
Default : false
-spec multiple_choices(rd(), state()) -> {boolean() | halt(), rd(), state()}.
If this returns true
, then it is assumed that multiple
representations of the response are possible and a single one cannot
be automatically chosen, so a 300 Multiple Choices
will be sent
instead of a 200 OK
.
Default : false
-spec previously_existed(rd(), state()) -> {boolean() | halt(), rd(), state()}.
If this returns true
, the moved_permanently
and
moved_temporarily
callbacks will be invoked to determine whether the
response should be 301 Moved Permanently
, 307 Temporary Redirect
,
or 410 Gone
.
Default : false
-spec moved_permanently(rd(), state()) -> {{true, URI::string()} | false | halt(), rd(), state()}.
If this returns {true, URI}
, the client will receive a 301 Moved Permanently
with URI
in the Location
header.
Default : false
-spec moved_temporarily(rd(), state()) -> {{true, URI::string()} | false | halt(), rd(), state()}.
If this returns {true, URI}
, the client will receive a 307 Temporary Redirect
with URI
in the Location
header.
Default : false
-spec last_modified(rd(), state()) -> {calendar:datetime() | undefined, rd(), state()}.
If this returns a datetime()
, it will be used for the
Last-Modified
header and for comparison in conditional requests.
Default : undefined
-spec generate_etag(rd(), state()) -> {string() | undefined, rd(), state()}.
If this returns a string()
, it will be used for the ETag
header
and for comparison in conditional requests.
Default : undefined
-spec finish_request(rd(), state()) -> {Result::any(), rd(), state()}.
This function, if exported, is called just before the final response
is constructed and sent. The Result
is ignored, so any effect of
this function must be by returning a modified ReqData
.
-spec Handler(rd(), state()) -> {iodata() | {stream, streambody()} | halt(), rd(), state()}.
This function must be named as a Handler
by
content_types_provided
. The Body
should be either an iodata()
or
{stream,streambody()}
.
-spec Handler(rd(), state()) -> {true | halt(), rd(), state()}.
This function must be named as a Handler
by content_types_accepted
and will be called when the request is PUT or when the request is POST
and post_is_create
returns true
.