Skip to content

tomeet/laravel-response

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

laravel-response

简体中文 | ENGLISH

为 Laravel 和 Lumen API 项目提供一个规范统一的响应数据格式。

Test StyleCI Latest Stable Version Total Downloads Monthly Downloads Latest Unstable Version License

社区讨论文章

介绍

laravel-response 主要用来统一 API 开发过程中「成功」、「失败」以及「异常」情况下的响应数据格式。

实现过程简单,在原有的 response()->json()进行封装,使用时不需要有额外的心理负担。

遵循一定的规范,返回易于理解的 HTTP 状态码,并支持定义 ResponseCodeEnum 来满足不同场景下返回描述性的业务操作码。

概览

  • 统一的数据响应格式,固定包含:codestatusdatamessageerror (响应格式设计源于:RESTful服务最佳实践 )
  • 你可以继续链式调用 JsonResponse 类中的所有 public 方法,比如 Response::success()->header('X-foo','bar');
  • 合理地返回 Http 状态码,默认为 restful 严格模式,可以配置异常时返回 200 http 状态码(多数项目会这样使用)
  • 支持格式化 Laravel 的 Api ResourceApi Resource CollectionPaginator(简单分页)、LengthAwarePaginator(普通分页)、Eloquent\ModelEloquent\Collection,以及简单的 arraystring等格式数据返回
  • 根据 debug 开关,合理返回异常信息、验证异常信息等
  • 支持修改 Laravel 特地异常的状态码或提示信息,比如将 No query results for model 的异常提示修改成 数据未找到
  • 支持配置返回字段是否显示,以及为她们设置别名,比如,将 message 别名设置为 msg,或者 分页数据第二层的 data 改成 list(res.data.data -> res.data.list)
  • 分页数据格式化后的结果与使用 league/fractal (DingoApi 使用该扩展进行数据转换)的 transformer 转换后的格式保持一致,也就是说,可以顺滑地从 Laravel Api Resource 切换到 league/fractal
  • 内置 Http 标准状态码支持,同时支持扩展 ResponseCodeEnum 来根据不同业务模块定义响应码(可选,需要安装 jiannei/laravel-enum)
  • 响应码 code 对应描述信息 message 支持本地化,支持配置多语言(可选,需要安装 jiannei/laravel-enum)

安装

支持 Laravel 5.5.* ~ Laravel 9.* 版本,自定义业务操作码部分依赖于 jiannei/laravel-enum,需要先进行安装。

laravel 版本 lumen 版本 response 版本 enum 版本
5.5.* 5.5.* ~1.8 ~1.4
6.* 6.* ^2.0 ~1.4
7.* 7.* ^3.0 ^2.0
8.* - 9.* 8.* - 9.* ^4.0 ^3.0
# laravel 5.5

composer require jiannei/laravel-response "~1.8" -vvv
composer require jiannei/laravel-enum "~1.4" -vvv # 可选

# laravel 6.x

composer require jiannei/laravel-response "^2.0" -vvv
composer require jiannei/laravel-enum "~1.4" -vvv # 可选

# laravel 7.x

composer require jiannei/laravel-response "^3.0" -vvv
composer require jiannei/laravel-enum "^2.0" -vvv # 可选

# laravel 8.x - 9.x

composer require jiannei/laravel-response "^4.0" -vvv
composer require jiannei/laravel-enum "^3.0" -vvv # 可选

配置

Laravel

  • 发布配置文件
$ php artisan vendor:publish --provider="Jiannei\Response\Laravel\Providers\LaravelServiceProvider"
  • 格式化异常响应
// app/Exceptions/Handler.php
// 引入以后对于 API 请求产生的异常都会进行格式化数据返回
// 要求请求头 header 中包含 /json 或 +json,如:Accept:application/json
// 或者是 ajax 请求,header 中包含 X-Requested-With:XMLHttpRequest;

<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
use Tomeet\Response\Laravel\Support\Traits\ExceptionTrait;

class Handler extends ExceptionHandler
{
    use ExceptionTrait;
    // ...
}

Lumen

  • 复制配置文件到 vendor/jiannei/laravel-response/config/response.php,到 config/response.php
cp vendor/jiannei/laravel-response/config/response.php config/response.php
  • 加载配置
// bootstrap/app.php
$app->configure('response');
  • 格式化异常响应

app/Exceptions/Handler.php 中 引入 use Tomeet\Response\Laravel\Support\Traits\ExceptionTrait;

app/Http/Controllers/Controller.php 中引入 use Tomeet\Response\Laravel\Support\Traits\ExceptionTrait;

  • 注册服务容器
$app->register(\Jiannei\Response\Laravel\Providers\LumenServiceProvider::class);

使用

扩展包本身提供了丰富的单元测试用例tests ,也可以通过查看测试用例来解锁使用方法。

或者查看相应的模板项目:

成功响应

示例代码

<?php
public function index()
{
    $users = User::all();

    return Response::success(new UserCollection($users));
}

public function paginate()
{
    $users = User::paginate(5);

    return Response::success(new UserCollection($users));
}

public function simplePaginate()
{
    $users = User::simplePaginate(5);

    return Response::success(new UserCollection($users));
}

public function item()
{
    $user = User::first();

    return Response::success(new UserResource($user));
}

public function array()
{
    return Response::success([
        'name' => 'Jiannel',
        'email' => 'longjian.huang@foxmail.com'
    ],'', ResponseCodeEnum::SERVICE_REGISTER_SUCCESS);
}

返回全部数据

支持自定义内层 data 字段名称,比如 rows、list

{
    "status": "success",
    "code": 200,
    "message": "操作成功",
    "data": {
        "data": [
            {
                "nickname": "Joaquin Ondricka",
                "email": "lowe.chaim@example.org"
            },
            {
                "nickname": "Jermain D'Amore",
                "email": "reanna.marks@example.com"
            },
            {
                "nickname": "Erich Moore",
                "email": "ernestine.koch@example.org"
            }
        ]
    },
    "error": {}
}

分页数据

支持自定义内层 data 字段名称,比如 rows、list

{
    "status": "success",
    "code": 200,
    "message": "操作成功",
    "data": {
        "data": [
            {
                "nickname": "Joaquin Ondricka",
                "email": "lowe.chaim@example.org"
            },
            {
                "nickname": "Jermain D'Amore",
                "email": "reanna.marks@example.com"
            },
            {
                "nickname": "Erich Moore",
                "email": "ernestine.koch@example.org"
            },
            {
                "nickname": "Eva Quitzon",
                "email": "rgottlieb@example.net"
            },
            {
                "nickname": "Miss Gail Mitchell",
                "email": "kassandra.lueilwitz@example.net"
            }
        ],
        "meta": {
            "pagination": {
                "count": 5,
                "per_page": 5,
                "current_page": 1,
                "total": 12,
                "total_pages": 3,
                "links": {
                    "previous": null,
                    "next": "http://laravel-api.test/api/users/paginate?page=2"
                }
            }
        }
    },
    "error": {}
}

返回简单分页数据

支持自定义内层 data 字段名称,比如 rows、list

{
    "status": "success",
    "code": 200,
    "message": "操作成功",
    "data": {
        "data": [
            {
                "nickname": "Joaquin Ondricka",
                "email": "lowe.chaim@example.org"
            },
            {
                "nickname": "Jermain D'Amore",
                "email": "reanna.marks@example.com"
            },
            {
                "nickname": "Erich Moore",
                "email": "ernestine.koch@example.org"
            },
            {
                "nickname": "Eva Quitzon",
                "email": "rgottlieb@example.net"
            },
            {
                "nickname": "Miss Gail Mitchell",
                "email": "kassandra.lueilwitz@example.net"
            }
        ],
        "meta": {
            "pagination": {
                "count": 5,
                "per_page": 5,
                "current_page": 1,
                "links": {
                    "previous": null,
                    "next": "http://laravel-api.test/api/users/simple-paginate?page=2"
                }
            }
        }
    },
    "error": {}
}

返回单条数据

{
    "status": "success",
    "code": 200,
    "message": "操作成功",
    "data": {
        "nickname": "Joaquin Ondricka",
        "email": "lowe.chaim@example.org"
    },
    "error": {}
}

其他快捷方法

Response::ok();// 无需返回 data,只返回 message 情形的快捷方法
Response::localize(200101);// 无需返回 data,message 根据响应码配置返回的快捷方法
Response::accepted();
Response::created();
Response::noContent();

失败响应

不指定 message

public function fail()
{
    Response::fail();// 不需要加 return
}
  • 未配置多语言响应描述
{
    "status": "fail",
    "code": 500,
    "message": "Http internal server error",
    "data": {},
    "error": {}
}
  • 配置多语言描述
{
    "status": "fail",
    "code": 500,
    "message": "操作失败",
    "data": {},
    "error": {}
}

指定 message

public function fail()
{
    Response::fail('error');// 不需要加 return
}

返回数据

{
    "status": "fail",
    "code": 500,
    "message": "error",
    "data": {},
    "error": {}
}

指定 code

public function fail()
{
    Response::fail('',ResponseCodeEnum::SERVICE_LOGIN_ERROR);
}

返回数据

{
    "status": "fail",
    "code": 500102,
    "message": "登录失败",
    "data": {},
    "error": {}
}

其他快捷方法

Response::errorBadRequest();
Response::errorUnauthorized();
Response::errorForbidden();
Response::errorNotFound();
Response::errorMethodNotAllowed();
Response::errorInternal();

异常响应

表单验证异常

{
    "status": "error",
    "code": 422,
    "message": "验证失败",
    "data": {},
    "error": {
        "email": [
            "The email field is required."
        ]
    }
}

Controller 以外抛出异常

可以使用 abort 辅助函数抛出 HttpException 异常

abort(500102,'登录失败');

// 返回数据

{
    "status": "fail",
    "code": 500102,
    "message": "登录失败",
    "data": {},
    "error": {}
}

其他异常

开启 debug(APP_DEBUG=true

{
    "status": "error",
    "code": 404,
    "message": "Http not found",
    "data": {},
    "error": {
        "message": "",
        "exception": "Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException",
        "file": "/home/vagrant/code/laravel-api-starter/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php",
        "line": 43,
        "trace": [
            {
                "file": "/home/vagrant/code/laravel-api-starter/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php",
                "line": 162,
                "function": "handleMatchedRoute",
                "class": "Illuminate\\Routing\\AbstractRouteCollection",
                "type": "->"
            },
            {
                "file": "/home/vagrant/code/laravel-api-starter/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
                "line": 646,
                "function": "match",
                "class": "Illuminate\\Routing\\RouteCollection",
                "type": "->"
            }
        ]
    }
}

关闭 debug

{
    "status": "error",
    "code": 404,
    "message": "Http not found",
    "data": {},
    "error": {}
}

自定义业务码

<?php
namespace App\Enums;

use Tomeet\Enum\Laravel\Repositories\Enums\HttpStatusCodeEnum;

class ResponseCodeEnum extends HttpStatusCodeEnum
{
    // 业务操作正确码:1xx、2xx、3xx 开头,后拼接 3 位
    // 200 + 001 => 200001,也就是有 001 ~ 999 个编号可以用来表示业务成功的情况,当然你可以根据实际需求继续增加位数,但必须要求是 200 开头
    // 举个栗子:你可以定义 001 ~ 099 表示系统状态;100 ~ 199 表示授权业务;200 ~ 299 表示用户业务。..
    const SERVICE_REGISTER_SUCCESS = 200101;
    const SERVICE_LOGIN_SUCCESS = 200102;

    // 客户端错误码:400 ~ 499 开头,后拼接 3 位
    const CLIENT_PARAMETER_ERROR = 400001;
    const CLIENT_CREATED_ERROR = 400002;
    const CLIENT_DELETED_ERROR = 400003;

    const CLIENT_VALIDATION_ERROR = 422001; // 表单验证错误

    // 服务端操作错误码:500 ~ 599 开头,后拼接 3 位
    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;
}

本地化业务码描述

<?php
// lang/zh_CN/enums.php
use App\Repositories\Enums\ResponseCodeEnum;

return [
    // 响应状态码
    ResponseCodeEnum::class => [
        // 成功
        ResponseCodeEnum::HTTP_OK => '操作成功', // 自定义 HTTP 状态码返回消息
        ResponseCodeEnum::HTTP_INTERNAL_SERVER_ERROR => '操作失败', // 自定义 HTTP 状态码返回消息
        ResponseCodeEnum::HTTP_UNAUTHORIZED => '授权失败',

        // 业务操作成功
        ResponseCodeEnum::SERVICE_REGISTER_SUCCESS => '注册成功',
        ResponseCodeEnum::SERVICE_LOGIN_SUCCESS => '登录成功',

        // 客户端错误
        ResponseCodeEnum::CLIENT_PARAMETER_ERROR => '参数错误',
        ResponseCodeEnum::CLIENT_CREATED_ERROR => '数据已存在',
        ResponseCodeEnum::CLIENT_DELETED_ERROR => '数据不存在',
        ResponseCodeEnum::CLIENT_VALIDATION_ERROR => '表单验证错误',

        // 服务端错误
        ResponseCodeEnum::SYSTEM_ERROR => '服务器错误',
        ResponseCodeEnum::SYSTEM_UNAVAILABLE => '服务器正在维护,暂不可用',
        ResponseCodeEnum::SYSTEM_CACHE_CONFIG_ERROR => '缓存配置错误',
        ResponseCodeEnum::SYSTEM_CACHE_MISSED_ERROR => '缓存未命中',
        ResponseCodeEnum::SYSTEM_CONFIG_ERROR => '系统配置错误',

        // 业务操作失败:授权业务
        ResponseCodeEnum::SERVICE_REGISTER_ERROR => '注册失败',
        ResponseCodeEnum::SERVICE_LOGIN_ERROR => '登录失败',
    ],
];

由 JetBrains 赞助

非常感谢 Jetbrains 为我提供的 IDE 开源许可,让我完成此项目和其他开源项目上的开发工作。

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Pham Thao

🎨

guanguans

🐛

This project follows the all-contributors specification. Contributions of any kind welcome!

Stargazers over time

Stargazers over time

协议

MIT 许可证(MIT)。有关更多信息,请参见协议文件

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages