我正在Erlang中编写REST服务,需要先验证接收到的数据,然后再将其传递给其他内部函数进行进一步处理;为了做到这一点,我目前正在使用嵌套的case
表达式:
case all_args_defined(Args) of
true ->
ActionSuccess = action(Args),
case ActionSuccess of
{ok, _} -> ...;
{fail, reason} -> {fail, reason}
end,
_ ->
{fail, "args not defined"}
end,
...
我意识到这有点难看,但这样我就可以提供详细的错误信息。另外,我不认为通常的使其崩溃哲学在这里适用 - 我不希望我的REST服务崩溃并且每次有人抛出无效参数时都会重新启动。
但是,我正在考虑放弃所有cases
支持保护try/catch
阻止任何badmatch
错误的问题 - 这会有效吗?
fun() ->
true = all_args_defined(Args),
{ok, _} = action(Args).
%% somewhere else
catch fun().
答案 0 :(得分:6)
由于您希望实现的是错误报告,因此您应该围绕执行操作和报告结果来构建事物。也许是这样的:
execute(Action, Args) ->
try
check_args(Args),
Result = action(Action, Args),
send_result(Result)
catch
throw:{fail, Reason} ->
report_error(Reason);
ExceptionClass:Term ->
%% catch-all for all other unexpected exceptions
Trace = erlang:get_stacktrace(),
report_error({crash, ExceptionClass, Term, Trace})
end.
%% all of these throw {fail, Reason} if they detect something fishy
%% and otherwise they return some value as result (or just crash)
action(foo, [X1, X2]) -> ...;
action(foo, Args) -> throw({fail, {bad_arity, foo, 2, Args}});
action(...) -> ...
%% this handles the formatting of all possible errors
report_error({bad_arity, Action, Arity, Args}) ->
send_error(io_lib:format("wrong number of arguments for ~w: "
"expected ~w, but got ~w",
[Action, Arity, length(Args)]));
report_error(...) -> ...;
report_error({crash, Class, Term, Trace}) ->
send_error(io_lib:format("internal error: "
"~w:~w~nstacktrace:~n~p~n",
[Class, Term, Trace])).
答案 1 :(得分:2)
我在开发创建用户的应用程序时遇到了这个问题。
我首先提出这样的解决方案:
insert() ->
try
check_1(), % the check functions throw an exception on error.
check_2(),
check_3(),
do_insert()
catch
throw:Error1 ->
handle_error_1();
throw:Error2 ->
handle_error_2();
_:Error ->
internal_error()
end.
此解决方案的问题在于您使用try ... catch块丢失了堆栈跟踪。 而不是这个,更好的解决方案是:
insert() ->
case catch execute() of
ok -> all_ok;
{FuncName, Error} ->
handle_error(FuncName, Error);
{'EXIT', Error} ->
internal_error(Error)
end.
execute() ->
check_1(), % the check functions throw an exception on error.
check_2(),
check_3(),
do_insert().
这样,您就可以在Error上获得完整的错误堆栈。
答案 2 :(得分:2)
在编写自己的REST服务时,我遇到了完全相同的问题。
让我们从哲学开始:
我喜欢把我的应用程序想象成一个盒子。在盒子的内侧是我建造的所有部件并且可以直接控制。如果有什么东西在这里破坏,这是我的错,它应该崩溃,我应该在错误日志中读到它。在框的边缘是与外界的所有连接点 - 这些都不值得信任。我避免在内部部件中进行异常处理,并根据外边缘的需要使用它。
我曾参与的类似项目:
我通常会对用户输入进行十几次检查。如果看起来不好,我会记录它并向用户返回错误。拥有堆栈跟踪对我来说并不是特别有意义 - 如果用户忘记了参数,我的代码中没有任何东西可以追捕并修复。我宁愿看到一个文本日志,上面写着:“在17:35,用户X访问路径Y但是缺少参数Z”。
我将检查组织成返回ok
或{error, string()}
的函数。 main函数只是迭代检查并返回ok
如果它们全部通过,否则返回第一个错误,然后记录。在我的检查功能中,我根据需要使用异常处理,因为我不可能考虑用户可以搞砸的所有方式。
正如我的同事所建议的那样,你也可以让每一个检查抛出一个异常,而不是使用一个元组。
至于您的实现,我认为如果您只进行一次检查,那么使用单个异常处理程序的想法是很好的。如果您最终需要更多检查,您可能希望实现我所描述的内容,以便您可以进行更具体的日志记录。