一種處理laravel返回值響應的解決方案

陳先生發表於2021-05-18
  • 我們假定一個場景,使用者註冊, 需要引數

    引數名 解釋 型別 是否必填
    mobile 使用者手機號碼 字串 必填
    sms_code 簡訊驗證碼 整數 必填
    password 使用者密碼 字串 使用者密碼
    re_password 重複使用者密碼 整數 重複使用者密碼
  • 以下列舉會出現的問題情況

    1. 使用者簡訊不匹配
    2. 使用者簡訊型別錯誤
    3. 使用者密碼不相同
    4. 手機號已經使用過 直接登入即可
    5. 使用者密碼型別不符合要求
    6. 簡訊驗證碼不能為空
    7. 手機號碼不能為空
    8. 兩個密碼都不能為空
  • 現在已知會複用的場景有 會在別的業務內有相同錯誤的型別 (具體業務不做贅述,業務不同,理解不同)

    1. 簡訊驗證 checkSms 驗證簡訊驗證碼是否正確 型別是否匹配
    2. 修改密碼 提示密碼型別錯誤等場景
  • 這裡假定大家都不是大佬 業務有藕合 處理方案如下 (以下程式碼僅在checkSms下進行)

    1. 在checkSms函式里面直接
      //第一次寫文件 不會用markdown 你也可以用 response出去  這樣淺顯易懂
      exit(json_encode(['code'=>-1,'msg'=>'簡訊驗證碼錯誤']));
    2. 每處都做判斷
      if (false === checkSms($mobile,$code,$type)){
        exit(json_encode(['code'=>-1,'msg'=>'簡訊驗證碼錯誤']));
      }
    3. 看看我的方式 (這句要怎麼加粗啊)
  • 已知你有三套業務 且每套業務包含N個子模組 (別槓微服務/跨語言等,槓就是你贏 /狗頭)

    1. 現在出了問題 前端告訴你 code=-1 message=>’系統錯誤 || 需要登入 || 商品查詢失敗 || 簡訊失敗 等各種錯誤資訊 ‘
      你什麼心情????????????? 開始到處找,這個message在哪, 誰寫的 ,什麼時候寫的,到底是哪個等
    2. 業務有藕合, 你在你的業務裡面用的某一個service(僅做偽例子)內的action 發現丟擲了一個你不清楚的異常,你去問,貼日誌
      結果必然是 你找的人去執行我上面說的那條,依次遞迴. 直到
       Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 9216 bytes) in your problem

      我就找不到在哪,我就是懵 日常維護老專案的同學豈不是人都沒了… 為什麼大家都是接盤,我卻過得這麼難
  • 與其到處去找不如直接告訴我是怎麼回事,但是你又很難保證大家寫的錯誤資訊內容都是一樣的

    1. 首先我們明白一個道理 你寫程式碼最害怕的是什麼 是bug嗎? 不是
      是這個

      你問我錯哪了? 我怎麼知道!!!!! 我就知道肯定報錯了 丟擲異常了 ,程式停了啊!!!

    2. ok 那麼我們知道了一個道理,當程式丟擲異常的時候,專案就會停掉.
      同時我們也明白了一件事 叫做:

    3. 假定我們有一個業務模組叫做 User 裡面包含了一個控制器叫做 AuthController
      內部需要完成一個login的行為

       <?php
      
       namespace App\Http\Controllers\Application\Auth;
      
       use App\Http\Controllers\Controller;
       use App\Http\Requests\Application\Auth\LoginRequest;
       use Illuminate\Http\JsonResponse;
      
       class AuthController extends Controller
       {
           //
           public function index(LoginRequest $loginRequest): JsonResponse
           {
               $data = $loginRequest->all();
               //todo 驗證是否登入成功
      
               //todo 登入成功之後需要從返回裡面獲取token 和 userInfo
      
               //todo 記錄日誌等行為
      
               //todo 返回前端 
               return $this->success('登陸成功', compact('token', 'user'));
           }
       }
      1. 一般會怎麼做
        1. if (!empty(login($username,$password))){ //todo 登入成功 //tonext}
      2. 一旦出錯 怎麼辦?????? 開始
          if(1){
              if(1){
                  if(1){
                      if(1){
                      //建議這裡直接用來測光速到底是多少 ,因為需求是無限長的
                      //並且你知道到底是什麼問題,什麼業務返回來的,到底的意思是什麼嘛? (突然成為派大星 )
                      }
                  }
              }
          }
      3. 如果你覺得 上面這個方案或者類似這個方案很棒,那我收回剛才那張圖
  • 我們上面已知程式丟擲異常就會停掉,除非你繼續catch 然後拋什麼出來??????怕不是萬能交稅
    1.我們設計讓我們的程式聽話,怎麼聽話,讓他犯錯自己會停,還會告訴你怎麼回事
    2.怎麼實現,這麼做的意義是什麼
    3.如何實現,這樣做有什麼別的意義沒
    4.效能損耗問題

  • 回答問題
    1.你是開發,程式是你的 你必須說什麼讓他聽什麼
    2.此處僅做流程展示,最後會直接貼程式碼加註釋,如果沒耐心可以直接翻最後 建立異常應該都ok吧,不ok 就去看文件 你可以停在這了

      <?php
    
      namespace App\Exceptions;
    
      use Exception;
    
      class XxxException extends Exception
      {
    
      /**
      * @Message('簡訊驗證碼錯誤')
      */  
      const SMS_CODE_IS_ERROR = '300000000';
    
      /**
      * @Message('簡訊驗證碼型別錯誤')
      */  
      const SMS_CODE_TYPE_IS_ERROR = '300000001';
    
      /**
      * @Message('簡訊驗證碼不存在')
      */  
      const SMS_CODE_IS_NOT_EXISTS = '300000002';
      }
    
      ?>
    1. 虛擬碼

      //todo 驗證碼型別錯誤
      throw new XxxException(XxxException::SMS_CODE_IS_ERROR);

      程式現在是不是應該停下來了,因為當你exception的時候 下面程式碼不會執行了
      但是新的問題出現了,如果這樣丟擲異常,前端怎麼辦???????????
      此處小聲bb 前端處理不了跟我什麼關係啊,我是後臺啊,你有問題找前端啊.
      那麼我們假設一下 如果我們告訴前端的是

      {code:'300000000','message:'簡訊驗證碼錯誤'}

      是不是就很舒服了,前後端是一家 怎麼能鬧脾氣呢
      那麼如果 Code統一,請問出問題你在發愁什麼? 是你的phpstorm不存在屬性追蹤嗎?

    2. 怎麼實現???????????????
      你問我,我也不到啊 我只能給你這個啊

        <?php
      
      namespace App\Exceptions;
      
      use App\Factory\ParseException; //解釋異常的工廠 嫌棄名字長就沒加Factory  
      use App\Traits\ResponseTrait; //這個是我自己寫的一個簡單的trait 也會一併貼上去
      use Illuminate\Database\Eloquent\ModelNotFoundException;
      use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
      use Illuminate\Http\JsonResponse;
      use Illuminate\Http\Response;
      use Illuminate\Support\Str;
      use Illuminate\Validation\ValidationException;
      use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
      use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
      use Throwable;
      
      class Handler extends ExceptionHandler
      {
        use ResponseTrait;
      
        /**
         * A list of the exception types that are not reported.
         *
         * @var array
         */
        protected $dontReport = [
            //
        ];
      
        /**
         * A list of the inputs that are never flashed for validation exceptions.
         *
         * @var array
         */
        protected $dontFlash = [
            'current_password',
            'password',
            'password_confirmation',
        ];
      
        /**
         * Register the exception handling callbacks for the application.
         *
         * @return void
         */
        public function register()
        {
            $this->reportable(function (Throwable $e) {
                //
            });
        }
      
        public function render($request, $e)
        {
            //資料庫沒查到資料或者資料是softdelete
            if ($e instanceof ModelNotFoundException) {
                return $this->error('500', '資料不存在或已刪除');
            }
            //非允許請求方式
            if ($e instanceof MethodNotAllowedHttpException) {
                return $this->error('422', '請求方式錯誤');
            }
            //驗證失敗
            if ($e instanceof ValidationException) {
                return $this->error(412, current(current($e->errors())));
            }
            //這裡是為了相容其他的一些錯誤
            if (Str::length($e->getMessage()) > 1 && Str::length($e->getCode()) > 1) {
                return $this->error($e->getCode(), $e->getMessage());
            }
            //處理我們自己的錯誤 
            $result = ParseException::parseException($e);
            //這裡判斷的原因很簡單 因為可能這個code沒有按照規範宣告 
            if (is_array($result)) {
                return $this->error($result['code'], $result['message']);
            }
            // Object Not Found  你懂我意思吧?
            if ($e instanceof NotFoundHttpException) {
                return $this->error('404', '頁面路徑不存在');
            }
            //這裡可以根據自己是否需要做兜底而決定是否兜底
        }
      }
      
      <?php
      
      namespace App\Factory;
      
      use Illuminate\Support\Facades\Log;
      
      class ParseException
      {
        public static function parseException(\Throwable $exception)
        {
        //註解 不懂得話建議直接看文件->反射 ,我講不明白這個東西
            $annotation = new \ReflectionClass($exception);
            //翻轉 成code->constant
            $values = array_flip($annotation->getConstants());
            if (empty($values)) {
                return false;
            }
            //拿到對應的constant
            $constant = $values[$exception->getMessage()];
            //constant反射
            $annotation_text = new \ReflectionClassConstant($exception, $constant);
            //獲取屬性註釋內容
            $comment = $annotation_text->getDocComment();
      
            try {
            //正則大法好 建議留意此處 
                preg_match("/Message\(\'(.*?)\'\)(\\r\\n|\\r|\\n)/U", $comment, $result);
            } catch (\Throwable $e) {
                return false;
            }
      
            if (false === isset($result[1])) {
                return false;
            }
            return [
                'code' => $exception->getMessage(),
                'message' => $result[1]
            ];
        }
      }

      不要問我要 ResponseTrait 我相信一個簡單的 響應實現你是ok的
      這樣實現的意義就是為了不管誰接手專案前端後端 看到錯誤資訊一目瞭然,就算某天領導說不要需要告訴使用者簡訊什麼錯了,就告訴他你簡訊錯了,你只需要去改constant而已!
      並且可讀性高,ide支援 ,如果你覺得不合適,那我沒轍了 ,我盡力了

    3. 效能損耗
      目前沒發現很明顯的效能損耗,給出的調優方案也是 如果可以的話註解的類的屬性列表(讓你留意的地方)可以做快取而已 ,(因為我目前不需要去考慮這個,laravels大法好)

  • 不出意外的話我的程式碼你拿著直接貼進去就可以用,但是我不建議你這麼做,因為一次吃飽不代表能一直吃飽,我希望你能清楚起碼也要點贊,不能白嫖這個道理

不要企圖假裝努力,因為結果不會陪你一起假裝~! (這個字的顏色怎麼改?)

本作品採用《CC 協議》,轉載必須註明作者和本文連結
技術是要靠自己邊學邊整活兒才行的

相關文章