plug_rest v0.10.0 PlugRest.Resource behaviour
Define callbacks and REST semantics for a Resource behaviour
Based on Cowboy’s cowboy_rest module. It operates on a Plug connection and a handler module which implements one or more of the optional callbacks.
For example, the route:
resource "/users/:username", MyApp.UserResource
will invoke the init/2
function of MyApp.UserResource
if it exists
and then continue executing to determine the state of the resource. By
default the resource must implement a to_html
content handler which
returns a “text/html” representation of the resource.
defmodule MyApp.UserResource do
use PlugRest.Resource
def init(conn, state) do
{:ok, conn, state}
end
def allowed_methods(conn, state) do
{["GET"], conn, state}
end
def resource_exists(%{params: params} = conn, _state)
username = params["username"]
# Look up user
state = %{name: "John Doe", username: username}
{true, conn, state}
end
def content_types_provided(conn, state) do
{[{"text/html", :to_html}], conn, state}
end
def to_html(conn, %{name: name} = state) do
{"<p>Hello, #{name}</p>", conn, state}
end
end
Each callback accepts a %Plug.Conn{}
struct and the current state
of the resource, and returns a three-element tuple of the form {value,
conn, state}
.
The resource callbacks are named below, along with their default values. Some functions are skipped if they are undefined. Others have no default value.
allowed_methods : ["GET", "HEAD", "OPTIONS"]
allow_missing_post : true
charsets_provided : skip
content_types_accepted : none
content_types_provided : [{{"text", "html", %{}}, :to_html}]
delete_completed : true
delete_resource : false
expires : nil
forbidden : false
generate_etag : nil
is_authorized : true
is_conflict : false
known_methods : ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]
languages_provided : skip
last_modified : nil
malformed_request : false
moved_permanently : false
moved_temporarily : false
multiple_choices : false
options : :ok
previously_existed : false
resource_exists : true
service_available : true
uri_too_long : false
valid_content_headers : true
valid_entity_length : true
variances : []
You must also define the content handler callbacks that are specified
through content_types_accepted/2
and content_types_provided/2
. It is
conventional to name the functions after the content types that they
handle, such as from_html
and to_html
.
The handler function which provides a representation of the resource
must return a three element tuple of the form {body, conn, state}
,
where body
is one of:
binary()
, which will be sent withsend_resp/3
{:chunked, Enum.t}
, which will usesend_chunked/2
{:file, binary()}
, which will usesend_file/3
You can halt the resource handling from any callback and return a manual response like so:
response = send_resp(conn, status_code, resp_body)
{:stop, response, state}
The content accepted handlers defined in content_types_accepted
will be
called for POST, PUT, and PATCH requests. By default, the response body will
be empty. If desired, you can set the response body like so:
conn2 = put_rest_body(conn, "#{conn.method} was successful")
{true, conn2, state}
Configuration
You can change some defaults by configuring the :plug_rest
app in
your config.exs
file.
To change the default known_methods
for all Resources:
config :plug_rest,
known_methods: ["GET", "HEAD", "OPTIONS", "TRACE"]
If a Resource implements the known_methods
callback, that list
always takes precedence over the default list.
Plug Pipeline
You can create a custom Plug pipeline within your resource using Plug.Builder
:
defmodule MessageResource do
use PlugRest.Resource
# Add the Builder to your resource
use Plug.Builder
# Add your custom plugs
plug :hello
# Finally, call the :rest plug to start executing the REST callbacks
plug :rest
# REST Callbacks
def to_html(conn, state) do
{conn.private.message, conn, state}
end
# Example custom plug function
def hello(conn, _opts) do
put_private(conn, :message, "Hello")
end
end
Summary
Functions
Returns the requested media type
Returns the REST response body if it has been set
Puts the media type in the connection
Manually sets the REST response body in the connection
Executes the REST state machine with a connection and resource
Callbacks
Returns whether POST is allowed when the resource doesn’t exist
Returns the list of allowed methods
Returns the list of charsets the resource provides
Returns the list of content-types the resource accepts
Returns the list of content-types the resource provides
Returns whether the delete action has been completed
Deletes the resource
Returns the date of expiration of the resource
Returns whether access to the resource is forbidden
Returns the entity tag of the resource
Sets up the connection and handler state before other REST callbacks
Returns whether the user is authorized to perform the action
Returns whether the put action results in a conflict
Returns the list of known methods
Returns the list of languages the resource provides
Returns the date of last modification of the resource
Returns whether the request is malformed
Returns whether the resource was permanently moved
Returns whether the resource was temporarily moved
Returns whether there are multiple representations of the resource
Handles a request for information
Returns whether the resource existed previously
Returns whether the resource exists
Returns whether the service is available
Returns whether the requested URI is too long
Returns whether the content-* headers are valid
Returns whether the request body length is within acceptable boundaries
Return the list of headers that affect the representation of the resource
Types
conn :: Plug.Conn.t
content_handler :: PlugRest.State.content_handler
content_type_p :: {binary | media_type, handler}
etag :: PlugRest.State.etag
handler :: PlugRest.State.handler
media_type :: PlugRest.State.media_type
opts :: Plug.opts
priority_type :: PlugRest.Conn.priority_type
quality_type :: PlugRest.Conn.quality_type
state :: PlugRest.State.t
status_code :: 200..503
Functions
Specs
get_media_type(conn) :: media_type | nil
Returns the requested media type
Specs
get_rest_body(conn) :: binary | nil
Returns the REST response body if it has been set
Specs
put_media_type(conn, media_type) :: conn
Puts the media type in the connection
Manually sets the REST response body in the connection
Callbacks
Returns whether POST is allowed when the resource doesn’t exist
Examples
def allow_missing_post(conn, state) do
{true, conn, state}
end
Returns the list of allowed methods
Examples
def allowed_methods(conn, state) do
{["GET,", "HEAD", "OPTIONS"], conn, state}
end
Returns the list of charsets the resource provides
Examples
def charsets_provided(conn, state) do
{["utf-8"], conn, state}
end
Returns the list of content-types the resource accepts
The list must be ordered in order of preference.
Each content-type can be given either as a binary string or as a tuple containing the type, subtype and parameters.
Examples
def content_types_accepted(conn, state) do
{[{"application/json", :from_json}], conn, state}
end
# post accepted
def from_json(conn, :success = state) do
conn = put_rest_body(conn, "{\"status\": \"ok\"}")
{true, conn, state}
end
# post create and redirect
def from_json(conn, :redirect = state) do
{{true, "new_url/1234"}, conn, state}
end
# post error
def from_json(conn, :error = state) do
{false, conn, state}
end
Returns the list of content-types the resource provides
Examples
def content_types_provided(conn, state) do
{[{"application/json", :to_json}], conn, state}
end
def to_json(conn, state) do
{"{}", conn, state}
end
Returns whether the delete action has been completed
Examples
def delete_completed(conn, state) do
{true, conn, state}
end
Specs
Returns the date of expiration of the resource
Examples
def expires(conn, state) do
{{{2012, 9, 21}, {22, 36, 14}}, conn, state}
end
Returns whether access to the resource is forbidden
Examples
def forbidden(conn, state) do
{false, conn, state}
end
Specs
Returns the entity tag of the resource
Examples
# ETag: W/"etag-header-value"
def generate_etag(conn, state) do
{{:weak, "etag-header-value"}, conn, state}
end
# ETag: "etag-header-value"
def generate_etag(conn, state) do
{{:strong, "etag-header-value"}, conn, state}
end
# ETag: "etag-header-value"
def generate_etag(conn, state) do
{"\"etag-header-value\""}, conn, state}
end
Sets up the connection and handler state before other REST callbacks
Examples
def init(conn, state) do
{:ok, conn, state}
end
Returns whether the user is authorized to perform the action
Examples
def is_authorized(conn, state) do
{true, conn, state}
end
Returns whether the put action results in a conflict
Examples
def is_conflict(conn, state) do
{false, conn, state}
end
Returns the list of known methods
Examples
def known_methods(conn, state) do
{["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
conn, state}
end
Returns the list of languages the resource provides
Examples
def languages_provided(conn, state) do
{["en"], conn, state}
end
Returns the date of last modification of the resource
Examples
def last_modified(conn, state) do
{{{2012, 9, 21}, {22, 36, 14}}, conn, state}
end
Returns whether the request is malformed
Examples
def malformed_request(conn, state) do
{false, conn, state}
end
Specs
Returns whether the resource was permanently moved
Examples
def moved_permanently(conn, state) do
{false, conn, state}
end
Specs
Returns whether the resource was temporarily moved
Examples
def moved_temporarily(conn, state) do
{false, conn, state}
end
Returns whether there are multiple representations of the resource
Examples
def multiple_choices(conn, state) do
{false, conn, state}
end
Handles a request for information
The response should inform the client the communication options available for this resource.
By default, Cowboy will send a 200 OK response with the allow header set.
Examples
def options(conn, state) do
{:ok, conn, state}
end
Returns whether the resource existed previously
Examples
def previously_existed(conn, state) do
{false, conn, state}
end
Returns whether the resource exists
Examples
def resource_exists(conn, state) do
{true, conn, state}
end
Returns whether the service is available
Examples
def service_available(conn, state) do
{true, conn, state}
end
Returns whether the requested URI is too long
Examples
def uri_too_long(conn, state) do
{false, conn, state}
end
Returns whether the content-* headers are valid
Examples
def valid_content_headers(conn, state) do
{true, conn, state}
end