Обрабатывать исключение из метода

#laravel #exception #laravel-controller

#laravel #исключение #laravel-контроллер

Вопрос:

Я осуществляю платежи для своего веб-сайта, используя API внешней службы (т.Е. служба поставщика платежей).

Допустим, пользователь нажимает «КУПИТЬ», а затем мы переходим к моему контроллеру, который говорит что-то вроде:

 public function buyFunction() {

    $result = $this->ExternalService->pay();

    if ($result->success == true) {
        return 'We are happy';
    }
}
 

Я также создал вышеупомянутый externalService , который имеет pay() метод:

 class ExternalService {

    public function pay() {
        response = //Do stuff with Guzzle to call the API to make the payment

        return response;
    }
}
 

Теперь иногда что-то идет не так.
Допустим, API возвращает ошибку, что означает, что он выдает исключение GuzzleException — как мне с этим справиться?

В идеале, если есть ошибка, я хотел бы зарегистрировать ее и перенаправить пользователя на страницу и сообщить ему, что что-то пошло не так.

Что я пробовал

  1. Я пытался использовать оператор try / catch в функции pay() и использовать abort(500) , но это не позволяет мне перенаправлять на нужную страницу.
  2. Я пытался использовать оператор try / catch в функции pay() и использовать return redirect('/mypage') , но это просто возвращает объект перенаправления контроллеру, который затем завершается с ошибкой при попытке вызова result->success
  3. Я попытался использовать номер 2, но также добавил блок try / catch в метод контроллера, но ничего не изменилось.

В конце концов, я нашел два решения. В обоих случаях я использую блок try / catch внутри метода pay(). Затем я либо return 0; проверяю контроллер if (result == 0) , либо использую abort( redirect('/mypage') ); внутри блока try / catch метода pay().

Как правильно с этим справиться? Как использовать блоки try / catch?

Комментарии:

1. Я бы предположил pay() , что функция должна перехватывать исключение Guzzle и создавать одно из ваших собственных (если у вас нет собственных классов исключений, ничего не делайте.) Затем в вашем контроллере вы поймаете исключение и перенаправите на сообщение об ошибке. Ваш контроллер обрабатывает взаимодействия с конечным пользователем, такие как прерывания или перенаправления, это не работа класса обработки платежей.

2. Просто примечание, я бы предложил следовать стандартному именованию классов, используя регистр pascal, поэтому ExternalService вместо ExternalService 🙂

3. @miken32 значит, каждая функция контроллера должна иметь блок try / catch? Я чувствую, что мне все время приходится использовать блоки try / catch

4. @miken32 Также в чем было бы преимущество создания моего собственного класса исключений вместо перехвата исключения Guzzle?

5. Это зависит от вашего варианта использования. У вас может быть несколько классов исключений, которые нужно отслеживать в контроллере, или у вас могут быть специальные действия, которые вы хотите выполнить при возникновении исключения. И если вы пишете код для использования третьими сторонами, лучше создавать свои собственные исключения.

Ответ №1:

По моему опыту, избегайте обработки исключений, пропускайте их и обрабатывайте их соответствующим образом с помощью try catches. Это наиболее прагматичный подход. В качестве альтернативы вы в конечном итоге проверите правильность результата в странных местах, например. if ($result) {...} . Просто предположим, что все прошло хорошо, за исключением случаев, когда генерируется исключение. Бонус: никогда не делайте перехват покемонов Exception $e , если вам это специально не нужно!

 class ExternalService {

    public function pay() {
        try {
            response = $client->get(...);
        } catch (BadResponseException $exception) {
            Log::warning('This should not happen check payment api: ' . $exception->getMessage());
            
            throw new PaymentException('Payment did not go through');
        } 

        return response;
    }
}
 

Предполагая, что у вас есть собственное исключение.

 class PaymentException extends HttpException
{
    public function __construct(?Exception $previous = null)
    {
        parent::__construct(Response::HTTP_BAD_REQUEST, 'Unexpected error processing the payment', $previous);
    }
}
 

Это позволяет обрабатывать поток в контроллере, где имело бы смысл обрабатывать перенаправление. Иногда, если исключение является очень неотъемлемым или общим для веб-приложения, оно также может обрабатываться обработчиком исключений.

 class PaymentController {
    public function pay(PaymentService $service) {
        try {
            $payment = $service->buyFunction();
        } catch (PaymentException $exception) {
            return redirect()->route('app.payment.error');
        } 
        
        return view('app.payment.success', compact('payment'));
    }
}
 

Комментарии:

1. Спасибо за ваш ответ. Мои вопросы: 1) Почему я должен создавать свое собственное пользовательское исключение, когда конкретное исключение GuzzleException уже существует? 2) Почему я не должен просто перехватывать общее исключение — например: catch (Exception $e) ? Таким образом, я могу включить больше кода в свой блок try / catch. И какая бы строка моего кода не прерывалась, я ее поймаю. 3) Действительно ли мне нужно добавлять блок try / catch в каждую строку? Я чувствую, что мне приходится часто его использовать. 4) Мой блок catch большой, и мой контроллер раздувается.

2. 1. Поскольку ваш пользователь не заботится о том, что ошибка не сработает, лучше обернуть его во что-то, с чем вам будет легче справиться, вы сможете использовать PaymentException во всех случаях, но исключения, о которых пользователь никогда не будет заботиться, и, как правило, считаются хорошей практикой и их легче отслеживать в таких системах, как bugsnag sentry и т. Д. Где исключение guzzle может быть много вещей. 2. Потому что, если вы создадите ошибку ArrayIndexOutOfBounds, она также обработает это, что может быть очень сложно отладить, если вы уверены, что странные исключения не проходят.

3. Спасибо. Кстати, вместо того, чтобы использовать блок try / catch в контроллере, вы можете обработать его в методе визуализации пользовательского исключения. Это более аккуратный IMO, поскольку вы не раздуваете свой контроллер блоками try / catch.

4. Да, вы можете 🙂 это зависит от случая, когда с этим нужно справиться, я думаю, что то, что контроллер делает по замыслу, выполняя некоторые попытки, не так уж плохо. Но если ошибка носит более общий характер, Handler.php это должно быть предпочтительнее.