Packages
nova
0.9.3
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}) ->
%% Ensure that the module exists and have the correct function exported
case lists:search(fun({Export, Arity}) -> Export == Function andalso
Arity == 1 end,
erlang:apply(Module,
module_info, [exports])) of
{value, _} ->
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;
_ ->
logger:error(#{msg => "Could not find controller",
controller => Module,
function => io_lib:format("~s/1", [Function])}),
Payload = #{status_code => 500,
status => "Problems with application",
stacktrace => [{Module, Function, 1}],
title => "Woops. It seems like you have a problem with your application!",
extra_msg => "Nova could not find your controller:function.
Pleae check your spelling and/or that the module" ++
" have been properly loaded. "
},
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.