В приложениях PHP / Laravel MVC коды ответов и тела часто определяются созданным исключением. Если выдается исключение HTTP (наследование от Symfony\Component\HttpKernel\Exception\HttpException
) выдаются правильные коды ответов (и в некоторых случаях ответы JSON). Существуют и другие типы исключений, которые также не связаны с http и могут быть выброшены.
Где следует генерировать исключения HTTP?
Должен ли я перехватывать свои исключения в контроллере и выдавать HTTP-версии этих исключений? Или я должен просто выбросить исключение HTTP где-нибудь глубоко внутри класса обслуживания, репозитория или утилиты, учитывая, что 99% приложений инфраструктуры MVC в любом случае основаны на жизненном цикле запроса HTTP >> ответа?
Мой ответ не нацелен на Laravel, так как я считаю, что работа с базовым мышлением идет вразрез с вашим первоначальным вопросом.
Всегда выбрасывайте специальное исключение а затем обработать преобразование в контроллере. В этом случае заверните его в HttpException
, Для этого есть несколько веских причин:
HttpException
броски в вашем сервисе не имеют смысла для вашей команды CLI.По сути, думая о калькуляторе, он бросил бы DivisionByZeroException
, Для контроллера вы бы обернули это в HttpException
400 BAD REQUEST
и перебросить. Для CLI ваша команда может просто позволить отображать исключение на экране Division By Zero
, В любом случае это решение не принимается вашей службой.
Где следует генерировать исключения HTTP?
Хотя это, как правило, зависит от предпочтений, сама структура, по-видимому, заняла по этому поводу самоуверенную позицию, и вы должны бросать их куда угодно. На самом деле у Laravel есть несколько полезных помощников, которые упрощают создание исключений с соответствующими кодами ответов:
abort(403, "Exception message"); //Will throw an HTTP exception with code 403
abort_if(true, 400, "Condition failed"); //Will throw a 400 error if the first parameter is true
abort_unless(false, 422, "Condition failed"); //Will throw a 422 error if the first parameter is false
Практический пример:
public function getById($id) {
$model = Model::find($id);
//These are equivalent
if ($model == null) {
abort(404, "$id not found");
}
abort_if($model == null, 404, "$id not found");
abort_unless($model != null, 404, "$id not found");
}
Это затронуто в Раздел обработки ошибок руководства
Обратите внимание, что abort
действительно вызывает исключения HTTP, так что вы все равно можете их перехватить и обработать, если вам нужно.
Похоже, существует общее недоразумение по этому вопросу. Насколько я понимаю, вопрос заключался в том, где следует генерировать исключения HTTP, но он переходит к более общей обработке исключений в контексте HTTP.
Прежде всего, если у вас есть исключение HTTP, то есть исключение, которое имеет смысл только в контексте цикла HTTP-запроса / ответа, вы должны быть в состоянии выбросить его там, где оно происходит, и не выбрасывать что-то другое с целью преобразования это когда он достигает контроллера, это то, что abort
помощники там делать.
Однако, если у вас есть исключение (любое исключение), которое следует интерпретировать с помощью специального кода ответа http, если оставить его необработанным, у вас есть варианты с этим справиться:
HttpException
(Это может показаться немного странным, что совершенно нормальное исключение наследуется от класса, который не имеет смысла вне жизненного цикла запроса / ответа).Реализовать render
метод в вашем исключении например.:
class SpecialException extends Exception {
public function render() {
return response()->view('errors.403', [], 403);
}
}
Имейте определенное поведение обработки в вашем \ App \ Exceptions \ Handler, например:
class Handler {
// ....
public function render($request, $exception) {
if ($exception instanceof SpecialException) {
return response()->view('errors.403', [], 403);
}
return parent::render()
}
}