Packages
nova
0.9.2
0.15.1
0.14.3
0.14.1
0.13.7
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.11
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.4.4
0.4.3
0.4.2
0.4.1
0.2.2
0.1.0
0.0.1
0.0.0
Nova is a web application framework
Current section
Files
Jump to
Current section
Files
src/nova_handler.erl
-module(nova_handler).
-behaviour(cowboy_middleware).
%% Callbacks
-export([
execute/2,
terminate/3
]).
-include_lib("kernel/include/logger.hrl").
-include("nova_router.hrl").
-callback init(Req, any()) -> {ok | module(), Req, any()}
| {module(), Req, any(), any()}
when Req::cowboy_req:req().
-callback terminate(any(), map(), any()) -> ok.
-optional_callbacks([terminate/3]).
-spec execute(Req, Env) -> {ok, Req, Env}
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
execute(Req, Env = #{cowboy_handler := Handler, arguments := Arguments}) ->
try erlang:apply(Handler, init, [Req, Arguments]) of
{ok, Req2, _State} ->
Result = terminate(normal, Req2, Handler),
{ok, Req2, Env#{result => Result}};
{Mod, Req2, State} ->
erlang:apply(Mod, upgrade, [Req2, Env, Handler, State]);
{Mod, Req2, State, Opts} ->
erlang:apply(Mod, upgrade, [Req2, Env, Handler, State, Opts])
catch Class:Reason:Stacktrace ->
Payload = #{status_code => 500,
stacktrace => Stacktrace,
class => Class,
reason => Reason},
logger:error(#{msg => "Controller crashed", class => Class, reason => Reason, stacktrace => Stacktrace}),
render_response(Req#{crash_info => Payload}, Env, 500)
end;
execute(Req, Env = #{module := Module, function := Function}) ->
try erlang:apply(Module, Function, [Req]) of
RetObj ->
case nova_handlers:get_handler(element(1, RetObj)) of
{ok, Callback} ->
{ok, Req0} = Callback(RetObj, {Module, Function}, Req),
render_response(Req0, Env);
{error, not_found} ->
logger:error(#{msg => "Controller returned unsupported result", controller => Module,
function => Function, return => RetObj})
end
catch Class:Reason:Stacktrace ->
logger:error(#{msg => "Controller crashed", class => Class, reason => Reason, stacktrace => Stacktrace}),
terminate(Reason, Req, Module),
%% Build the payload object
Payload = #{status_code => 500,
stacktrace => Stacktrace,
class => Class,
reason => Reason},
render_response(Req#{crash_info => Payload}, Env, 500)
end.
-spec terminate(any(), Req | undefined, module()) -> ok when Req::cowboy_req:req().
terminate(Reason, Req, Module) ->
case erlang:function_exported(Module, terminate, 3) of
true ->
erlang:apply(Module, terminate, [Reason, Req]);
false ->
ok
end.
%%%%%%%%%%%%%%%%%%%%%%%%
%% INTERNAL FUNCTIONS %%
%%%%%%%%%%%%%%%%%%%%%%%%
-spec render_response(Req :: cowboy_req:req(), Env :: map()) -> {ok, Req :: cowboy_req:req(), State :: map()}.
render_response(Req = #{resp_status_code := StatusCode}, Env) ->
Req0 = cowboy_req:reply(StatusCode, Req),
{ok, Req0, Env};
render_response(Req, Env) ->
Req0 = cowboy_req:reply(200, Req),
{ok, Req0, Env}.
render_response(Req, Env, StatusCode) ->
case application:get_env(nova, render_error_pages, true) of
true ->
case nova_router:lookup_url(StatusCode) of
{error, _} ->
%% Render the internal view of nova
{ok, Req0} = nova_basic_handler:handle_status({status, StatusCode}, {dummy, dummy}, Req),
render_response(Req0, Env);
{ok, _Bindings, #nova_handler_value{module = EMod, function = EFunc}} ->
%% Recurse to the execute function to render error page
execute(Req, Env#{app => nova, module => EMod, function => EFunc})
end;
false ->
{ok, Req0} = nova_basic_handler:handle_status({status, StatusCode}, {dummy, dummy}, Req),
render_response(Req0, Env)
end.