由於目前網站後台使用 Laravel 標準的 view 製作,但前台將使用 API 串接,這時候若沒有做錯誤處理就會發現 Laravel 拋出的錯誤訊息夾帶著 view 視圖的錯誤訊息,有心人士就可以透過這些訊息得知你的 API 是用哪種方式生成,利用漏洞來攻擊你的 API。

統一 API 資料及訊息格式

在 app\Traits 目錄,建立 ApiResponser.php 檔案。

<?php

namespace App\Traits;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Validator;
use Illuminate\Pagination\LengthAwarePaginator;

trait ApiResponser
{
    //成功時回應格式
    protected function successResponse($total, $data, $message = null, $code = 200)
    {
        return response()->json([
            'status'=> 'Success',
            'message' => $message,
            'total' => $total,
            'data' => $data
        ], $code);
    }
    //失敗時回應格式
    protected function errorResponse($message = null, $code)
    {
        return response()->json([
            'status'=>'Error',
            'message' => $message,
        ], $code);
    }
}

修改 app\Http\Controller\Controller.php 或者另外建立一支延伸的 ApiController.php,基本上都是一樣的。

<?php
namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use App\Traits\ApiResponser;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests, ApiResponser;
}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Traits\ApiResponser;

class ApiController extends Controller
{
    use ApiResponser;
}

在 API Controller 中使用 ApiResponser

//之前寫法
return response()->json(['status' => 'Success','message' => '成功','total' => $products->count(), 'data' => $products],200);
return response()->json(['status' => 'Error', 'message' => '參數不存在/參數錯誤'],400);

//使用 ApiResponser 寫法
return $this->successResponse($products->count(), $products, '成功');
return $this->errorResponse('參數不存在/參數錯誤', 400);

錯誤處理

修改 app\Exceptions\Handler.php

<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;

use App\Traits\ApiResponser;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\HttpException;

class Handler extends ExceptionHandler
{
    use ApiResponser;

    /**
     * Register the exception handling callbacks for the application.
     *
     * @return void
     */
    public function register()
    {
        $this->reportable(function (Throwable $e) {
            //
        });
        // Laravel 8.x 改成此種註冊方式
        $this->renderable(function (Throwable $e) {
            return $this->handleException($e);
        });
    }

    //建立 handleException function
    public function handleException( Throwable $e){
        if ($e instanceof MethodNotAllowedHttpException) {
            return $this->errorResponse('The specified method for the request is invalid', 405);
        }
        if ($e instanceof NotFoundHttpException) {
            return $this->errorResponse('The specified URL cannot be found', 404);
        }
        if ($e instanceof HttpException) {
            return $this->errorResponse($e->getMessage(), $e->getStatusCode());
        }
        if (config('app.debug')) {
            return parent::render($request, $e);
        }
        return $this->errorResponse('Unexpected Exception. Try later', 500);
    }
}

這樣一來,所有的錯誤都將返回成文字型態錯誤訊息,讓人無法探測你的網站 或 API 的任何資訊。

測試

對應到錯誤處理的 MethodNotAllowedHttpException
對應到錯誤處理的 NotFoundHttpException
最後修改日期: 2021 年 5 月 16 日