Response 响应
这里不使用 dingo 进行开发,个人感觉不怎好用,我们下面自己定义
首先,我们需要在 app
目录下创建一个 Helpers
目录
一、封装统一状态码(ResponseEnum)
在 app/Helpers
目录下创建 ResponseEnum.php
文件
<?php
namespace App\Helpers;
class ResponseEnum
{
// 001 ~ 099 表示系统状态;100 ~ 199 表示授权业务;200 ~ 299 表示用户业务
/*-------------------------------------------------------------------------------------------*/
// 100开头的表示 信息提示,这类状态表示临时的响应
// 100 - 继续
// 101 - 切换协议
/*-------------------------------------------------------------------------------------------*/
// 200表示服务器成功地接受了客户端请求
const HTTP_OK = [200001, '操作成功'];
const HTTP_ERROR = [200002, '操作失败'];
const HTTP_ACTION_COUNT_ERROR = [200302, '操作频繁'];
const USER_SERVICE_LOGIN_SUCCESS = [200200, '登录成功'];
const USER_SERVICE_LOGIN_ERROR = [200201, '登录失败'];
const USER_SERVICE_LOGOUT_SUCCESS = [200202, '退出登录成功'];
const USER_SERVICE_LOGOUT_ERROR = [200203, '退出登录失败'];
const USER_SERVICE_REGISTER_SUCCESS = [200104, '注册成功'];
const USER_SERVICE_REGISTER_ERROR = [200105, '注册失败'];
const USER_ACCOUNT_REGISTERED = [23001, '账号已注册'];
/*-------------------------------------------------------------------------------------------*/
// 300开头的表示服务器重定向,指向的别的地方,客户端浏览器必须采取更多操作来实现请求
// 302 - 对象已移动。
// 304 - 未修改。
// 307 - 临时重定向。
/*-------------------------------------------------------------------------------------------*/
// 400开头的表示客户端错误请求错误,请求不到数据,或者找不到等等
// 400 - 错误的请求
const CLIENT_NOT_FOUND_HTTP_ERROR = [400001, '请求失败'];
const CLIENT_PARAMETER_ERROR = [400200, '参数错误'];
const CLIENT_CREATED_ERROR = [400201, '数据已存在'];
const CLIENT_DELETED_ERROR = [400202, '数据不存在'];
// 401 - 访问被拒绝
const CLIENT_HTTP_UNAUTHORIZED = [401001, '授权失败,请先登录'];
const CLIENT_HTTP_UNAUTHORIZED_EXPIRED = [401200, '账号信息已过期,请重新登录'];
const CLIENT_HTTP_UNAUTHORIZED_BLACKLISTED = [401201, '账号在其他设备登录,请重新登录'];
// 403 - 禁止访问
// 404 - 没有找到文件或目录
const CLIENT_NOT_FOUND_ERROR = [404001, '没有找到该页面'];
// 405 - 用来访问本页面的 HTTP 谓词不被允许(方法不被允许)
const CLIENT_METHOD_HTTP_TYPE_ERROR = [405001, 'HTTP请求类型错误'];
// 406 - 客户端浏览器不接受所请求页面的 MIME 类型
// 407 - 要求进行代理身份验证
// 412 - 前提条件失败
// 413 – 请求实体太大
// 414 - 请求 URI 太长
// 415 – 不支持的媒体类型
// 416 – 所请求的范围无法满足
// 417 – 执行失败
// 423 – 锁定的错误
/*-------------------------------------------------------------------------------------------*/
// 500开头的表示服务器错误,服务器因为代码,或者什么原因终止运行
// 服务端操作错误码:500 ~ 599 开头,后拼接 3 位
// 500 - 内部服务器错误
const SYSTEM_ERROR = [500001, '服务器错误'];
const SYSTEM_UNAVAILABLE = [500002, '服务器正在维护,暂不可用'];
const SYSTEM_CACHE_CONFIG_ERROR = [500003, '缓存配置错误'];
const SYSTEM_CACHE_MISSED_ERROR = [500004, '缓存未命中'];
const SYSTEM_CONFIG_ERROR = [500005, '系统配置错误'];
// 业务操作错误码(外部服务或内部服务调用)
const SERVICE_REGISTER_ERROR = [500101, '注册失败'];
const SERVICE_LOGIN_ERROR = [500102, '登录失败'];
const SERVICE_LOGIN_ACCOUNT_ERROR = [500103, '账号或密码错误'];
const SERVICE_USER_INTEGRAL_ERROR = [500200, '积分不足'];
//501 - 页眉值指定了未实现的配置
//502 - Web 服务器用作网关或代理服务器时收到了无效响应
//503 - 服务不可用。这个错误代码为 IIS 6.0 所专用
//504 - 网关超时
//505 - HTTP 版本不受支持
/*-------------------------------------------------------------------------------------------*/
}
二、创建业务异常捕获 Exception 文件
在 app/Exceptions
目录下创建 BusinessException.php
文件用于业务异常的抛出
<?php
namespace App\Exceptions;
use Exception;
class BusinessException extends Exception
{
/**
* 业务异常构造函数
* @param array $codeResponse 状态码
* @param string $info 自定义返回信息,不为空时会替换掉codeResponse 里面的message文字信息
*/
public function __construct(array $codeResponse, $info = '')
{
[$code, $message] = $codeResponse;
parent::__construct($info ?: $message, $code);
}
}
三、自定义返回异常
修改 app/Exceptions
目录下的 Handler.php
文件
<?php
namespace App\Exceptions;
use App\Helpers\ApiResponse;
use App\Helpers\ResponseEnum;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;
class Handler extends ExceptionHandler
{
use ApiResponse;
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array<int, string>
*/
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, Throwable $exception)
{
// 如果是生产环境则返回500
if (!config('app.debug')) {
$this->throwBusinessException(ResponseEnum::SYSTEM_ERROR);
}
// 请求类型错误异常抛出
if ($exception instanceof MethodNotAllowedHttpException) {
$this->throwBusinessException(ResponseEnum::CLIENT_METHOD_HTTP_TYPE_ERROR);
}
// 参数校验错误异常抛出
if ($exception instanceof ValidationException) {
$this->throwBusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR);
}
// 路由不存在异常抛出
if ($exception instanceof NotFoundHttpException) {
$this->throwBusinessException(ResponseEnum::CLIENT_NOT_FOUND_ERROR);
}
// 自定义错误异常抛出
if ($exception instanceof BusinessException) {
return response()->json([
'status' => 'fail',
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
'data' => null,
'error' => null,
]);
}
return parent::render($request, $exception);
}
}
四、封装 API 返回的统一消息(ApiResponse)
在 app/Helpers
目录下创建 ApiResponse.php
文件
<?php
namespace App\Helpers;
use App\Helpers\ResponseEnum;
use App\Exceptions\BusinessException;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
trait ApiResponse
{
/**
* 成功
* @param null $data
* @param array $codeResponse
* @return JsonResponse
*/
public function success($data = null, $codeResponse=ResponseEnum::HTTP_OK): JsonResponse
{
return $this->jsonResponse('success', $codeResponse, $data, null);
}
/**
* 失败
* @param array $codeResponse
* @param null $data
* @param null $error
* @return JsonResponse
*/
public function fail($codeResponse=ResponseEnum::HTTP_ERROR, $data = null, $error=null): JsonResponse
{
return $this->jsonResponse('fail', $codeResponse, $data, $error);
}
/**
* json响应
* @param $status
* @param $codeResponse
* @param $data
* @param $error
* @return JsonResponse
*/
private function jsonResponse($status, $codeResponse, $data, $error): JsonResponse
{
list($code, $message) = $codeResponse;
return response()->json([
'status' => $status,
'code' => $code,
'message' => $message,
'data' => $data ?? null,
'error' => $error,
]);
}
/**
* 成功分页返回
* @param $page
* @return JsonResponse
*/
protected function successPaginate($page): JsonResponse
{
return $this->success($this->paginate($page));
}
private function paginate($page)
{
if ($page instanceof LengthAwarePaginator){
return [
'total' => $page->total(),
'page' => $page->currentPage(),
'limit' => $page->perPage(),
'pages' => $page->lastPage(),
'list' => $page->items()
];
}
if ($page instanceof Collection){
$page = $page->toArray();
}
if (!is_array($page) && !is_object($page)){
return $page;
}
$total = count($page);
return [
'total' => $total, //数据总数
'page' => 1, // 当前页码
'limit' => $total, // 每页的数据条数
'pages' => 1, // 最后一页的页码
'list' => $page // 数据
];
}
/**
* 业务异常返回
* @param array $codeResponse
* @param string $info
* @throws BusinessException
*/
public function throwBusinessException(array $codeResponse=ResponseEnum::HTTP_ERROR, string $info = '')
{
throw new BusinessException($codeResponse, $info);
}
}
五、创建控制器基类
1、在 app/Http/controller
目录下创建一个 BaseController.php
作为 Api 的基类,然后在 BaseController.php
这个基类中继承 Controller,并引入封装 API 返回的统一消息(ApiResponse)
<?php
namespace App\Http\Controllers;
use App\Helpers\ApiResponse;
class BaseController extends Controller
{
// API接口响应
use ApiResponse;
}
六、使用
1、返回成功信息
return $this->success($data);
2、返回失败信息
return $this->fail($codeResponse);
3、抛出异常
$this->throwBusinessException($codeResponse);
4、分页
return $this->successPaginate($data);
创建 API 表单请求验证基类
每当接收用户提交的参数时,都需要对数据做验证,以保证数据的准确性,接下来创建属于 Api 的表单请求验证类:
需要创建一个基类,方便做一些统一方法的封装,对于接口的验证类,我们也统一放在 Api 目录中。
$ php artisan make:request Api/FormRequest
app/Http/Requests/Api/FormRequest.php
<?php
namespace App\Http\Requests\Api;
use Illuminate\Foundation\Http\FormRequest as BaseFormRequest;
class FormRequest extends BaseFormRequest
{
public function authorize()
{
return true;
}
}
题外话
我自己在写api的时候,为了区分api的用户。往往会将api的目录设置为以下这样。用不同的命名空间在区分接口。比如小程序所有的api接口会放在Api/Weapp
中,会让代码结构更清晰。
Api/Weapp //代表的是小程序的api接口
Api/Mobile //代表的是移动端App的api接口
Api/Web //代表的是网站的api接口
如果这样的话,那么上面的基类路径需要稍微修改一下。这样更加合理一些。
然后创建一个我们的控制器基类
php artisan make:controller Api/Weapp/Controller
修改一下
app/Http/Controllers/Api/Weapp/Controller.php
<?php
namespace App\Http\Controllers\Api\Weapp;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller as BaseController;
use App\Helpers\ApiResponse;
class Controller extends BaseController
{
// API接口响应
use ApiResponse;
}