Laravel工程化项目二:创建一个带参数验证的接口

326 阅读2分钟

上一节

正文

Docker构建本地开发环境

使用项目中的Dockerfile文件构建本地环境,项目目录执行如下命令即可:

docker build -t laravel-project -f Dockerfile .

docker run -d --name laravel-project -itv 你的本机项目绝对路径:/var/www/html -p 11081:80 laravel-project

执行成功后,通过 docker ps 检查是否启动成功,如下即为创建成功了,这里宿主机端口为11081

docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED         STATUS                           PORTS                                     NAMES
c3732957703e   laravel-project   "/usr/bin/supervisor…"   2 seconds ago   Up 1 second (health: starting)   0.0.0.0:11081->80/tcp, :::11081->80/tcp   laravel-project

统一接口响应

因为是提供api服务,开始项目之前先定义接口公共响应结构,在app/Http/Controller/Controller中定义方法如下:

/**
 * 接口成功
 * @param array $data
 * @param string $msg
 * @return array
 */
public function apiSuccess(array $data = [], string $msg = 'success'): array
{
    return [
        'msg' => $msg,
        'code' => Constants::API_CODE_SUCCESS,
        'data' => $data
    ];
}

/**
 * 接口失败
 * @param string $msg
 * @param int $code
 * @param array $data
 * @return array
 */
public function apiFail(string $msg = 'api fail', int $code = Constants::API_CODE_FAIL, array $data = []): array
{
    return [
        'msg' => $msg,
        'code' => $code,
        'data' => $data
    ];
}

/**
 * 自定义响应
 * @param string $msg
 * @param int $code
 * @param array $data
 * @return array
 */
public function apiResponse(string $msg = '', int $code = 0, array $data = []): array
{
    return [
        'msg' => $msg,
        'code' => $code,
        'data' => $data
    ];
}

我们定义返回体为msg、code、data三个字段,code=200代表成功

新增Controller

 php artisan make:controller Api/IndexController

随便加一个方法

public function index()
{
    return $this->apiSuccess([
        'name'=> 'jack'
    ]);

}

新增Route

修改routes/api.php,路由的ServiceProvider在app/Providers/RouteServiceProvider.php


Route::namespace("App\Http\Controllers\Api")->prefix("v1")->group(function () {
    Route::get("index", [IndexController::class, 'index']);
});

image.png

接口参数校验

场景:用户登录 通过api/v1/login 传入username/password完成登录校验,现在没有人在controller里面通过if(empty($username))去校验参数了, 参数校验通过Request完成,主要校验参数值的有效性

php artisan make:request Index/LoginRequest

代码如下

class LoginRequest extends BaseRequest
{

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'username' => 'required|min:1',
            'password' => 'required|min:1'
        ];
    }

    public function messages()
    {
        return [
            'username.required' => '用户名不能为空',
            'username.min'=> '用户名格式错误',
            'password.required' => '密码不能为空',
            'password.min'=> '密码格式错误',

        ];
    }
}

在Controller中就可以使用Request进行参数校验了,validated()方法返回校验通过的参数数组


/**
 * @param LoginRequest $loginRequest
 * @return array
 */
public function login(LoginRequest $loginRequest): array
{
    $params = $loginRequest->validated();

    return $this->apiSuccess([
        'name' => $params['username']
    ]);

}

这时我们访问api/v1/login如果参数不合法就会通过LoginRequest抛出异常

异常错误正确返回给客户端

默认的错误处理会被渲染到页面展示出来

image.png

框架所有的错误被app/Exception/handler.php 捕获后会渲染到客户端,但是我们作为api服务规定通过统一格式返回,因此通过改造app/Exception/handler.php来实现。

handler有两个重要方法,render跟report render用来渲染框架的异常给客户端,report用来提交给系统处理一般是记录到日志中

  • 改造render,给客户端返回json异常数据
public function render($request, Throwable $e)
{
    if ($e instanceof HttpException) {
        $code = $e->getStatusCode();
    } else {
        $code = $e->getCode() ?: Constants::API_CODE_FAIL;
    }
    return response()->json([
        'msg' => $e->getMessage(),
        'code' => $code,
        'data' => []
    ]);
}

通过api/v1/login进行测试

QQ20210910-113037@2x.png

参数校验失败的错误信息

默认情况下Request通过抛出ValidationException 提示参数校验失败,但是我们看不到具体的错误信息,他看起来是这样的,不知道哪个参数不对

image.png

  • 改造Request,我们把validateException的第一个错误拿出来返回给客户端就可以了

class BaseRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    protected function failedValidation(Validator $validator)
    {
        $errors = $validator->errors()->all();
        throw new HttpException(Constants::API_CODE_FAIL, $errors[0]);
    }

}


这样看起来比较清晰了!

image.png

记录日志

Laravel使用Monolog来记录日志,由于单个日志文件会很大不方便查看,这里我们的策略是多channel按日期记录日志。

logging 的配置文件在app/config/loggin.php

  • 新增channel channel通常根据需求自定义即可,建议根据业务流程来增加,比如下单流程新增一个channel这样方便后期的检索。

这里使用daily_api_index为cahnnel,一共记录30个文件,一天一个。loggin.php


'daily_api_index' => [
    'driver' => 'daily',
    'path' => '/var/log/application/api.log',
    'level' => env('LOG_LEVEL', 'debug'),
    'days' => 30,
],

  • 使用日志

public function login(LoginRequest $loginRequest): array
{
    $params = $loginRequest->validated();

    Log::channel("daily_api_index")->info("get-request", [$params]);

    return $this->apiSuccess([
        'name' => $params['username']
    ]);

}

日志记录如下所示


/var/log/application $ tail -f daily_api_index-2021-09-10.log
[2021-09-10 03:50:46] local.INFO: get-request [{"username":"jack","password":"jack"}]

总结

这里我们通过新增一个新接口使其能正确访问,并对日志、异常处理、接口响应做了响应的优化。

文末福利

qrcode_for_gh_4e759f8e7269_344.jpg