Packages

HTTP Erlang JSON APIs - library for easy API management

Current section

Files

Jump to
heja src heja_dispatch.erl
Raw

src/heja_dispatch.erl

%% Description: heja_dispatch
%%
-module(heja_dispatch).
-behaviour(elli_handler).
%% elli_handler callbacks
-export([handle/2, handle_event/3]).
%% elli_handler callbacks -----------------------------------------------------
handle(Req, [Ref]) ->
case valid_content_type(Req) of
false ->
{400, [json_header()],
lejson:encode(#{error=>content_type_not_acceptable})};
true ->
dispatch_to_handler(Req, Ref)
end.
dispatch_to_handler(Req, Ref) ->
Method = elli_request:method(Req),
Path = elli_request:path(Req),
Args = elli_request:get_args(Req),
case heja_api:get_handler(Ref, method_to_lower(Method), Path) of
{ok, Fun, Bindings} ->
Body = decode_body(Req),
Context = atomize_keys(maps:merge(maps:from_list(Args), Bindings)),
case catch Fun(Body, Context) of
{ok, Resp} when is_map(Resp) ->
{200, [json_header()], lejson:encode(Resp)};
{ok, {text, Resp}} when is_binary(Resp) ->
{200, [text_header()], Resp};
{error, Error} ->
{400, [json_header()], lejson:encode(#{error=>Error})};
{'EXIT', {Error, _}} ->
{500, [json_header()],
lejson:encode(#{error=>handler_failure,
details=>Error})};
_ ->
{500, [json_header()],
lejson:encode(#{error=>handler_failure,
details=>unsupported_return})}
end;
{error, method_not_supported} ->
{400, [json_header()], lejson:encode(#{error=>method_not_allowed})};
{error, not_found} ->
{400, [json_header()], lejson:encode(#{error=>not_found})}
end.
handle_event(_Event, _Data, _Args) ->
ok.
method_to_lower('GET') -> get;
method_to_lower('POST') -> post;
method_to_lower('PUT') -> put;
method_to_lower('DELETE') -> delete;
method_to_lower(Other) -> Other.
decode_body(Req) ->
case elli_request:body(Req) of
<<>> ->
#{};
Bin ->
lejson:decode(Bin)
end.
json_header() ->
{<<"Content-Type">>, <<"application/json">>}.
text_header() ->
{<<"Content-Type">>, <<"text/plain">>}.
valid_content_type(Req) ->
Type = proplists:get_value(<<"Content-Type">>, elli_request:headers(Req)),
case elli_request:method(Req) of
'POST' ->
%% FIXME: Should check if Type is prefix
is_json_type(Type);
'PUT' ->
is_json_type(Type);
_ ->
true
end.
is_json_type(<<"application/json",_/binary>>) -> true;
is_json_type(_) -> false.
atomize_keys(Map) when is_map(Map) ->
maps:fold(fun(K, V, M) ->M#{to_atom(K)=>atomize_keys(V)} end, #{}, Map);
atomize_keys(Value) ->
to_atom(Value).
to_atom(T) when is_binary(T) -> binary_to_atom(T, utf8);
to_atom(T) when is_list(T) -> list_to_atom(T);
to_atom(T) when is_atom(T) -> T.