2016-07-30 4 views
4

У меня есть контроллер laravel, который может генерировать исключение и глобальное промежуточное программное обеспечение, которое ловит это исключение. В полу-псевдокоде:Исключение из контроллера в промежуточном программном обеспечении

// App\Controllers\... 
class Controller { 
    function store() { 
    throw new FormException; // via validation etc, but it's thrown here 
    } 
} 

// App\Http\Middleware\... 
class Middleware { 
    function handle(Closure $next) { 
    try { 
     // Breakpoint 1 
     return $next(); // $response 
     // Breakpoint 2 
    } 
    catch (FormException $ex) { 
     // Breakpoint 3 
     exit('FormException caught!'); 
    } 
    } 
} 

Проблема в том, что исключение никогда не попадается. Где-то в конвейере приложение выхватывает исключение и печатает красивую страницу с ошибками, но это должно быть уловлено моим промежуточным программным обеспечением, чтобы оно могло корректно обрабатывать его.

  • Breakpoint 1 должен вызвать, и это делает < < хорошо
  • Breakpoint 2 не должно вызывать, и она не < < хорошо
  • Breakpoint 3 должен вызвать, но это не < < какие??

Единственный способ, которым я могу представить себе, что мое промежуточное ПО не улавливает его, - это если он поймал где-то глубже внутри конвейера, а не дальше/вокруг, но я не могу найти попытку/улов в другом промежуточном программном обеспечении или в код выполнения трубопровода.

Куда это исключение попало? Зачем?

Это может быть не очень хороший образец, но сейчас меня это не волнует. Мне больше любопытно, чем что-либо еще. Я полностью неправильно понимаю промежуточное ПО Laravel?

Мой собственный супер простой тест промежуточного слоя делает то, что я ожидал: https://3v4l.org/Udr84 - catch и handle исключение внутри промежуточного программного обеспечения.

Примечание:

  • $response объекта (возвращаемое значение $next()) является перевалено страницей исключения, так что уже обработаны. Где и почему?
  • Работа с исключением в App\Exceptions\Handler::render() работает, но я хочу, чтобы вся логика была в пакете промежуточного программного обеспечения, а не в коде приложения.

Соответствующий Laravel код:

  • Kernel::handle() запускает промежуточный трубопровод < < это имеет всеохватывающий улов(), но мой улов() приходит первое, верно?
  • Pipeline::then() начнется выполнение MiddleWare
  • Pipeline::getSlice() ручки и создает $next затворы

ответ

11

Видимо this is by design:

Да, это beavhiour начиная с L5.2. Выброс исключения приводит к тому, что ответ должен быть установлен как возвращаемый из обработчика исключений, а затем промежуточное ПО разрешено выполнять резервное копирование с этой точки.

Я думаю, что это очень странно. Плагируемое промежуточное ПО было бы идеально подходит для обнаружения исключений.

Два способа сделать это еще:

  • Надлежащая: в App\Exceptions\Handler, что не достаточно хорошо, потому что пакет не может прикоснуться к
  • Funky: take the original exception object from the response object:

    $response = $next($request); 
    $exception = $response->exception; 
    
+0

Я согласен: обработка исключений в промежуточном программном обеспечении была бы полезна в пакетах: я разрабатываю промежуточное программное обеспечение для обработки транзакций базы данных, и мне нужно совершить/откат от промежуточного программного обеспечения. Быть в состоянии поймать исключения было бы очень полезно. Теперь я должен пойти «напуганным» способом;) – Moppo

+0

Большое спасибо Rudie. '** Funky way **' спас мой день :) Хорошо работает на Laravel ** v5.5.33 ** –

-2

Я думаю, что я могу понять, почему ваш код не перехватывать исключения. Пожалуйста, попробуйте использовать следующий код для метода ручки:

function handle(Closure $next) { 
try { 
    // Breakpoint 1 
    $response = $next(); 
    // Breakpoint 2 
} 
catch (FormException $ex) { 
    // Breakpoint 3 
    exit('FormException caught!'); 
} 
return $response; 
} 

Код выше не был проверен, но, как вы смотрите на документацию Laravel вы можете увидеть, прежде чем вернуться в ответ, вы должны выполнить свой код (в данном случае , ваша логика обработки исключений). Пожалуйста, просмотрите: Laravel - Defining Middleware для получения дополнительной информации о До & После определения промежуточного программного обеспечения.

И, кстати, также посмотрите этот файл: Laravel/app/Exceptions/Handler.php, который, я считаю, является лучшим местом для обработки ваших исключений по всему миру.

+0

Не это. Нет никакого исключения, чтобы быть пойманным. Это поймано где-то в другом месте. Объект '$ response' в моем обработчике - это страница, на которой отображается обработанное исключение. Где и зачем это обрабатывается? – Rudie

+0

Работа с ним в обработчике приложения работает, но мне нужен пакет промежуточного программного обеспечения. Вот почему существует промежуточное ПО, не так ли? Делать что-то до и после действия. – Rudie

2

У меня была та же проблема. При чтении thread Rudie упоминалось, они дают возможность решения там, который работал для меня:

public function handle(Request $request, Closure $next) { 
    $response = $next($request); 

    // 'Catch' our FormValidationException and redirect back. 
    if (!empty($response->exception) && $response->exception instanceof FormValidationException) { 
    return redirect()->back()->withErrors($response->exception->form->getErrors())->withInput(); 
    } 

    return $response; 
} 
+0

Вот что я получил и от Github. Это просто слишком противно для меня. Исключения должны быть пойманы, правильно? Не извлекается из объекта Response. В зависимости от вашего варианта использования вы можете отредактировать обработчик обработчика исключений приложения. – Rudie

+0

@ Rudie Я полностью согласен, это неожиданное поведение. Но я использую обработчик исключений по умолчанию для моего базового приложения. Для моей группы маршрутов api я хочу обрабатывать разные ошибки, тогда этот метод работает. – Jetse

0

Как отлавливать ошибки, не касаясь App\Exceptions\Handler файла:

Регистрация вашего CustomExceptionHandler

/* @var ExceptionHandler Illuminate\Contracts\Debug\ExceptionHandler */ 
$previousHandler = null; 
if (app()->bound(ExceptionHandler::class) === true) { 
    $previousHandler = app()->make(ExceptionHandler::class); 
} 
app()->singleton(ExceptionHandler::class, function() use ($previousHandler) { 
    return new CustomExceptionHandler($previousHandler); 
}); 

И ваша основная CustomExceptionHandler

class CustomExceptionHandler implements ExceptionHandlerInterface 
{ 
    /** 
    * @var ExceptionHandlerInterface|null 
    */ 
    private $previous; 

    public function __construct(ExceptionHandlerInterface $previous = null) 
    { 
     $this->previous = $previous; 
    } 

    public function report(Exception $exception) 
    { 
     $this->previous === null ?: $this->previous->report($exception); 
    } 

    public function render($request, Exception $exception) 
    { 
     if ($exception instanceof CustomExceptionHandler) { 
      echo 'This is my particular way to show my errors'; 
     } else { 
      $response = $this->previous === null ? null : $this->previous->render($request, $exception); 
     } 

     return $response; 
    } 

    /** 
    * {@inheritdoc} 
    */ 
    public function renderForConsole($output, Exception $exception) 
    { 
     /* @var OutputInterface $output */ 
     $this->previous === null ?: $this->previous->renderForConsole($output, $exception); 
    } 
} 
+0

Только 1 пакет может это сделать. Что делать, если 2 пакета хотят добавить обработку исключений для собственного исключения? По крайней мере, это может вызвать зависающий метод '$ response-> exception'. У каждого есть свое преимущество. – Rudie

Смежные вопросы