aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Björklund <[email protected]>2020-09-11 12:35:36 +0200
committerLoïc Hoguin <[email protected]>2020-11-27 16:17:43 +0100
commit8795233c57f1f472781a22ffbf186ce38cc5b049 (patch)
tree140478621afdef80d4957e17e417a2751ef7694c
parent63a6b86fbae1190ba78dceb196588fdcae50e17a (diff)
downloadcowboy-master.tar.gz
cowboy-master.tar.bz2
cowboy-master.zip
AcceptCallback may now return created/see_other tuples for POSTHEADmaster
They replace and deprecate the {true,URI} return value.
-rw-r--r--doc/src/manual/cowboy_rest.asciidoc17
-rw-r--r--src/cowboy_rest.erl8
-rw-r--r--test/handlers/create_resource_h.erl28
-rw-r--r--test/rest_handler_SUITE.erl24
4 files changed, 73 insertions, 4 deletions
diff --git a/doc/src/manual/cowboy_rest.asciidoc b/doc/src/manual/cowboy_rest.asciidoc
index a445948..0bb6d47 100644
--- a/doc/src/manual/cowboy_rest.asciidoc
+++ b/doc/src/manual/cowboy_rest.asciidoc
@@ -86,7 +86,10 @@ normal::
----
AcceptCallback(Req, State) -> {Result, Req, State}
-Result :: true | {true, URI :: iodata()} | false}
+Result :: true
+ | {created, URI :: iodata()}
+ | {see_other, URI :: iodata()}
+ | false
Default - crash
----
@@ -99,11 +102,14 @@ For PUT requests, the body is a representation of the resource
that is being created or replaced.
For POST requests, the body is typically application-specific
-instructions on how to process the request, but it may also
-be a representation of the resource. When creating a new
-resource with POST at a different location, return `{true, URI}`
+instructions on how to process the request, but it may also be a
+representation of the resource. When creating a new resource with POST
+at a different location, return `{created, URI}` or `{see_other, URI}`
with `URI` the new location.
+The `see_other` tuple will redirect the client to the new location
+automatically.
+
For PATCH requests, the body is a series of instructions on
how to update the resource. Patch files or JSON Patch are
examples of such media types.
@@ -724,6 +730,9 @@ listed here, like the authorization header.
== Changelog
+* *2.9*: An `AcceptCallback` can now return `{created, URI}` or
+ `{see_other, URI}`. The return value `{true, URI}`
+ is deprecated.
* *2.7*: The media type wildcard in `content_types_accepted`
is now documented.
* *2.6*: The callback `rate_limited` was added.
diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl
index 468f9ab..7d0fe80 100644
--- a/src/cowboy_rest.erl
+++ b/src/cowboy_rest.erl
@@ -1104,6 +1104,14 @@ process_content_type(Req, State=#state{method=Method, exists=Exists}, Fun) ->
next(Req2, State2, fun maybe_created/2);
{false, Req2, State2} ->
respond(Req2, State2, 400);
+ {{created, ResURL}, Req2, State2} when Method =:= <<"POST">> ->
+ Req3 = cowboy_req:set_resp_header(
+ <<"location">>, ResURL, Req2),
+ respond(Req3, State2, 201);
+ {{see_other, ResURL}, Req2, State2} when Method =:= <<"POST">> ->
+ Req3 = cowboy_req:set_resp_header(
+ <<"location">>, ResURL, Req2),
+ respond(Req3, State2, 303);
{{true, ResURL}, Req2, State2} when Method =:= <<"POST">> ->
Req3 = cowboy_req:set_resp_header(
<<"location">>, ResURL, Req2),
diff --git a/test/handlers/create_resource_h.erl b/test/handlers/create_resource_h.erl
new file mode 100644
index 0000000..f82e610
--- /dev/null
+++ b/test/handlers/create_resource_h.erl
@@ -0,0 +1,28 @@
+-module(create_resource_h).
+
+-export([init/2]).
+-export([allowed_methods/2]).
+-export([resource_exists/2]).
+-export([content_types_accepted/2]).
+-export([from_text/2]).
+
+init(Req, Opts) ->
+ {cowboy_rest, Req, Opts}.
+
+allowed_methods(Req, State) ->
+ {[<<"POST">>], Req, State}.
+
+resource_exists(Req, State) ->
+ {true, Req, State}.
+
+content_types_accepted(Req, State) ->
+ {[{{<<"application">>, <<"text">>, []}, from_text}], Req, State}.
+
+from_text(Req=#{qs := Qs}, State) ->
+ NewURI = [cowboy_req:uri(Req), "/foo"],
+ case Qs of
+ <<"created">> ->
+ {{created, NewURI}, Req, State};
+ <<"see_other">> ->
+ {{see_other, NewURI}, Req, State}
+ end.
diff --git a/test/rest_handler_SUITE.erl b/test/rest_handler_SUITE.erl
index 43695c3..1667565 100644
--- a/test/rest_handler_SUITE.erl
+++ b/test/rest_handler_SUITE.erl
@@ -52,6 +52,7 @@ init_dispatch(_) ->
{"/content_types_accepted", content_types_accepted_h, []},
{"/content_types_provided", content_types_provided_h, []},
{"/delete_resource", delete_resource_h, []},
+ {"/create_resource", create_resource_h, []},
{"/expires", expires_h, []},
{"/generate_etag", generate_etag_h, []},
{"/if_range", if_range_h, []},
@@ -474,6 +475,29 @@ delete_resource_missing(Config) ->
{response, _, 500, _} = gun:await(ConnPid, Ref),
ok.
+create_resource_created(Config) ->
+ doc("POST to an existing resource to create a new resource. "
+ "When the accept callback returns {created, NewURI}, "
+ "the expected reply is 201 Created."),
+ ConnPid = gun_open(Config),
+ Ref = gun:post(ConnPid, "/create_resource?created", [
+ {<<"content-type">>, <<"application/text">>}
+ ], <<"hello">>, #{}),
+ {response, _, 201, _} = gun:await(ConnPid, Ref),
+ ok.
+
+create_resource_see_other(Config) ->
+ doc("POST to an existing resource to create a new resource. "
+ "When the accept callback returns {see_other, NewURI}, "
+ "the expected reply is 303 See Other with a location header set."),
+ ConnPid = gun_open(Config),
+ Ref = gun:post(ConnPid, "/create_resource?see_other", [
+ {<<"content-type">>, <<"application/text">>}
+ ], <<"hello">>, #{}),
+ {response, _, 303, RespHeaders} = gun:await(ConnPid, Ref),
+ {_, _} = lists:keyfind(<<"location">>, 1, RespHeaders),
+ ok.
+
error_on_malformed_accept(Config) ->
doc("A malformed Accept header must result in a 400 response."),
do_error_on_malformed_header(Config, <<"accept">>).