[erlang-questions] Cowboy routes with Method as a condition

530 views
Skip to first unread message

Max Lapshin

unread,
Feb 10, 2013, 10:49:32 AM2/10/13
to Erlang-Questions Questions
Is it possible to specify cowboy routes with method (GET/POST) as a routing condition?

Loïc Hoguin

unread,
Feb 10, 2013, 6:06:01 PM2/10/13
to Max Lapshin, Erlang-Questions Questions
Cowboy routes to resources, on which operations are applied depending on method/headers/body, so no. cowboy_rest is what you most likely want.

Max Lapshin

unread,
Feb 11, 2013, 3:01:55 AM2/11/13
to Loïc Hoguin, Erlang-Questions Questions
cowboy_rest is incredibly hard to use because there is even no full list of callbacks. Webmachine diagram is good, but it lacks name of callbacks.

I understand that it is hard to document it, but I can't help you with it because I don't understand this code in depth.

I want to put Amazon S3 API in routes and it looks like rest is a bad idea. I've ended with catch-all route and manual routing in init/3 handler

Olav Frengstad

unread,
Feb 13, 2013, 3:39:37 AM2/13/13
to Max Lapshin, Erlang-Questions Questions
I use a small wrapper over cowboy_http_hander that let's me bind a
method to a function call.

method_handlers(Req, State) ->
[{<<"GET">>, read}, {<<"POST">>, update}, Req, State].

read(Req, State) ->
{Req, State}.

update(Req, State) ->
{Req, State}.

The easiest making handle/2 call handle_method(Method, Req, State) or similar.

If you want something pluggable you can look at https://github.com/lafka/tavern

Olav

2013/2/11 Max Lapshin <max.l...@gmail.com>:
> _______________________________________________
> erlang-questions mailing list
> erlang-q...@erlang.org
> http://erlang.org/mailman/listinfo/erlang-questions
>



--
Med Vennlig Hilsen
Olav Frengstad

Systemutvikler // FWT
+47 920 42 090
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions

Max Lapshin

unread,
Feb 13, 2013, 3:58:57 AM2/13/13
to Olav Frengstad, Erlang-Questions Questions
It is great, but I think that it would be a good idea to add some more data to Constraints.

Loic, if you think that it is good idea, I can add patch so that there will be 'method' in Constraints for better routing. M?

Loïc Hoguin

unread,
Feb 13, 2013, 4:00:04 AM2/13/13
to Max Lapshin, Erlang-Questions Questions

Constraints act on bindings.

--
Loïc Hoguin
Erlang Cowboy
Nine Nines
http://ninenines.eu

Max Lapshin

unread,
Feb 13, 2013, 4:05:39 AM2/13/13
to Loïc Hoguin, Erlang-Questions Questions
I understand it.

But look at rails routing: you can add constraints not only on bindings, but also on methods. It is very convenient, because it allows CRUD  routing from routes and it makes code smaller due to removing protection of GET method.

If I add route:

"/items/:item/destroy", item_handler, [destroy]

than I need to check if this method is POST, because it is a very bad idea to make any destructive action on GET.
If I add:

"items/:item/destroy", [{method,<<"POST">>}], item_handler, [destroy]

I don't need to write useless code.

Why are you against it?
Let's use not  'method', but <<"method">> or any other name that will not clash with bindings.

Loïc Hoguin

unread,
Feb 13, 2013, 4:27:48 AM2/13/13
to Max Lapshin, Erlang-Questions Questions
Rails is a framework, Cowboy is trying hard not to be.

HTTP has a long section about mapping URIs to resources on which you
perform actions. Actions are identified by methods, but aren't used to
find the resource to be used. Cowboy simply follows HTTP.

Adding ways to use methods to map to handlers or functions directly into
Cowboy would just break this concept of resources that Cowboy is trying
hard to protect.

It shouldn't be difficult to write a middleware that allows you to do
this efficiently if that's what you want, or fork the current router to
add the functionality, but that's something that won't land in Cowboy
master.

Max Lapshin

unread,
Feb 13, 2013, 5:18:18 AM2/13/13
to Loïc Hoguin, Erlang-Questions Questions
Ok, I hear you, but I don't understand you.

It is a very strange decision for me, because I usually prefer to select design choices that make programming convenient for programmers.

Middleware is a very bad design choice, because it requires duplicating code between configuration of router and middleware and it will end as throwing away cowboy_router at all.


Loïc Hoguin

unread,
Feb 13, 2013, 5:29:10 AM2/13/13
to Max Lapshin, Erlang-Questions Questions
On 02/13/2013 11:18 AM, Max Lapshin wrote:
> Ok, I hear you, but I don't understand you.
>
> It is a very strange decision for me, because I usually prefer to select
> design choices that make programming convenient for programmers.

That's an odd statement, I'm not sure why you would think that's not
what I'm doing. What I'm not doing, however, is make easy things easier
and hard things harder, like Rails does.

> Middleware is a very bad design choice, because it requires duplicating
> code between configuration of router and middleware and it will end as
> throwing away cowboy_router at all.

Router is middleware configuration so that's probably okay.

What I don't really understand is why you need this feature at all
considering you can already have a clause per method easily (which is
fairly similar to a function per method). All you have to do is:

handle(Req, State) ->
handle_method(cowboy_req:get(method, Req), Req, State).

So I'm not sure what you are expecting. If this doesn't work out then
there's plenty other solutions, but I don't know which one is best for
your case.

Max Lapshin

unread,
Feb 13, 2013, 5:29:54 AM2/13/13
to Loïc Hoguin, Erlang-Questions Questions
I can give you an example:

URL  /users/15

GET /users/15  should be routed to function that shows user
DELETE /users/15  should be route to function that destroys user

Currently you are forcing user to spread routing among different places:

"/users/:user_id", user_handler, []  and there, in init I need to check if this method is GET or DELETE or anything else

but with method constraints, it will work as following:

"/users/:user_id", [{method,<<"GET">>}], user_handler, [show]
"/users/:user_id", [{method,<<"DELETE">>}], user_handler, [destroy]


I don't see any HTTP pure theory violation in it. It looks rather convenient and more clear than checking which method was passed by user.

Max Lapshin

unread,
Feb 13, 2013, 5:34:31 AM2/13/13
to Loïc Hoguin, Erlang-Questions Questions
handle(Req, State) ->
    handle_method(cowboy_req:get(method, Req), Req, State).  will require


handle_method(<<"GET">>, Req, State)
handle_method(<<"DELETE">>, Req, State)
handle_method(Other, Req, State)
  here we copy from module to module default reply on "method not supported" or 404, whatever we choose.


with method constraints code can look so:


init(_, Req, [Action]) ->
 {ok, Req, Action}.


handle(Req, show) ->
  ..

handle(Req, destroy) ->
  ..

handle(Req, list) ->
  ..
  .

And no "default handler" because it is not required. Only good requests can pass to this handler.


Loïc Hoguin

unread,
Feb 13, 2013, 5:39:00 AM2/13/13
to Max Lapshin, Erlang-Questions Questions

Then in your init function do something like your_lib:route_method(Req)
which returns {ok, Req, Action} if it's good or {shutdown, ...} if not?
It'll do exactly what you just said.

Max Lapshin

unread,
Feb 13, 2013, 5:42:34 AM2/13/13
to Loïc Hoguin, Erlang-Questions Questions
Loic, my idea about routing is that router is a piece of software that translates input http request into presentation, convenient for handler and selects proper handler for this with proper arguments.

I don't understand your logic: you allow to run regexp to validate if constraint is a digit:   "/users/:user_id", [{user_id,fun() -> .. end}], but you are strongly against validating method in constraints.

You tell me to spread logic between your router and my router. You tell me that I need to use two routers that will somehow interfere with each other. What is the idea? I really don't understand.

Loïc Hoguin

unread,
Feb 13, 2013, 6:02:05 AM2/13/13
to Max Lapshin, Erlang-Questions Questions
The HTTP RFC says this on page 6:

It builds on the discipline of reference
provided by the Uniform Resource Identifier (URI) [3], as a location
(URL) [4] or name (URN) [20], for indicating the resource to which a
method is to be applied.

It then defines resources as follow on page 9:

A network data object or service that can be identified by a URI,
as defined in section 3.2. Resources may be available in multiple
representations (e.g. multiple languages, data formats, size, and
resolutions) or vary in other ways.

It then describes how to locate resources in many of the following
sections, starting with section 3.2 page 18.

Cowboy implements this. It maps URIs to resources. It makes absolutely
no assumptions as to how your resources operate. Anything that is part
of the URI can and will be used to locate resources. But if it's not
part of the URI, then it's not something used to find resources as
defined by the HTTP standard, which makes it out of the scope of the
project.

It still provides you with many ways to plug yourself into Cowboy to
achieve what you want, which include:

* Middleware
* Protocol upgrade (what REST handlers are doing)
* init/3 based method dispatching
* handle/2 based method dispatching

And I'm sure there's more.

On 02/13/2013 11:42 AM, Max Lapshin wrote:
> Loic, my idea about routing is that router is a piece of software that
> translates input http request into presentation, convenient for handler
> and selects proper handler for this with proper arguments.
>
> I don't understand your logic: you allow to run regexp to validate if
> constraint is a digit: "/users/:user_id", [{user_id,fun() -> .. end}],
> but you are strongly against validating method in constraints.
>
> You tell me to spread logic between your router and my router. You tell
> me that I need to use two routers that will somehow interfere with each
> other. What is the idea? I really don't understand.
>
>
> On Wed, Feb 13, 2013 at 2:39 PM, Loïc Hoguin <es...@ninenines.eu
> <mailto:es...@ninenines.eu>> wrote:
>
> On 02/13/2013 11:34 AM, Max Lapshin wrote:
>
> handle(Req, State) ->

> handle_method(cowboy_req:get(__method, Req), Req, State).

Vladimir Dronnikov

unread,
Feb 13, 2013, 9:44:05 AM2/13/13
to Loïc Hoguin, Erlang-Questions Questions
Max, one could insert a middleware before cowboy_router which mangle URI to be Method/URI and then in the router rules treat Method chunk as binding -- hence, method constraints.

--Vladimir

Vladimir Dronnikov

unread,
Feb 14, 2013, 9:13:29 AM2/14/13
to Loïc Hoguin, Erlang-Questions Questions

Max Lapshin

unread,
Feb 14, 2013, 9:18:51 AM2/14/13
to Vladimir Dronnikov, Erlang-Questions Questions
Thank you!
Reply all
Reply to author
Forward
0 new messages