你们在开发Api的时候有将Laravel进行统一Api响应格式吗?

现在看大家几乎总是用固定格式
code
message
data
这种格式,但是
网站上的教程是没有的

不过这种格式,我看就这个code有一定的价值,自定义业务逻辑编码吧
但是业务逻辑简单定义这些code感觉意义不大,业务复杂后,定义每个code你们又是怎么定义的呢?

另外,laravel内置的resource好像对这种支持不太好,Laravel的resourceCollection好象本身就是针对Api进行格式化的
像分页这种再格式化那意义不是没啥用了

可不可以针对一些业务操作,用固定格式,像列表啊这些仍然保留ResourceCollection

《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 8

在过去的经历中,也基本是以 code/msg/data 这三种结构,对于部分情况,比如没有权限、需要登录这些场景,应该算是返回不同 code 的最常见场景,当然,你也可以根据 HTTP Status Code 来检查。

其次,定义 code 的主要目的在于一些场景下,光靠返回错误信息可能不足以引导用户在前端做出一些操作,可以让前端根据不同的 code 来做一些事情,比如,显示引导、弹窗等等。

因为 msg 还要考虑国际化,所以用 code 来判断显然是更好的一种方案。

其次就是 msg,实际上,大部分时候可能都用不上,用得上的反倒是当出现错误的时候,返回一些错误信息,所以可以看到一些 api 设计中,其实是使用 erron/errors 之类的,而不是 msg,因为成功的 msg 没有太大的意义,前端也可以自己做成功的反馈,不依赖后端,而错误信息则要依赖后端了。

而在一些 API 的设计中,甚至都没有 code/msg,而是使用 success 来进行判断,或者直接判断 errors。

data 的争议其实比前两者还要大,像在 axios,如果使用 laravel 默认的分页结构,再加上这个 data ,前端需要访问数据时,就要用 resp.data.data.data 才能访问到,有一些抽象了,即便是封装过的,也要访问两层 data。

在一些设计中, 就主张将 data 放在根节点,meta 这些也提取出去,就像 jsonapi 规范这样,laravel 的 Resource 就偏向于这种,当然,你也可以自行定制。

之前在项目中有用到过 Laravel Resource,我个人觉得不是很好用,现在在用 spatie/laravel-data + code/msg/data/errors 的方式(目前 errors 设计的有些问题,之前做的是直接把 validator 的结果填充到 errors 的)。

控制器直接返回 Resource 或者 Data 愿景是好的,但是在之前的使用中,还是觉得不太合适。

2个月前 评论

需要前端特别处理的,或者特别常用的才会定义code,否则都会固定的code,前端只需要把msg的警告内容弹出来就可以了。我一般用这个包:github.com/jiannei/laravel-respons...

2个月前 评论
Mutoulee 2个月前

我们是 statu_code + data +message

2个月前 评论

你们讲的这个CODE我是明白 但是laavel要统一这个格式还挺麻烦的,比如说表单验证的错误信息异常你要自己拦截 分页信息用ResourceCollection后格式不是code message data这种,要自己再次包装很麻烦,除非你不用ResourceCollection格式化Api数据

那部分自己控制器调用$this->succcess这种来响应的话是可以统一,但是前端就的处理多种模式了

2个月前 评论

我都是自己在继承的控制器里面写个success,fail方法,再自己写个ResourceTrait对分页,列表,详情之类的做格式化。

app\Http\Controllers\Controller.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 Symfony\Component\HttpFoundation\Response;
use App\Services\HttpService;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    /**
     * @param $data
     * @param string $msg
     * @return array
     */
    public function success($data = null, $msg = '成功')
    {
        return HttpService::response($data, $msg, Response::HTTP_OK);
    }

    /**
     * @param $msg
     * @param int $code
     * @return array
     */
    public function fail($msg, $code = Response::HTTP_BAD_REQUEST)
    {
        return HttpService::error($msg, $code);
    }
}

app\Services\HttpService.php

<?php

namespace App\Services;

use Symfony\Component\HttpFoundation\Response;

class HttpService {
    public static function response($data, $msg, $code) {
        return response()->json([
            'code' => $code,
            'msg' => $msg,
            'data' => $data,
        ]);
    }

    public static function success($data) {
        return self::response($data, '操作成功', Response::HTTP_OK);
    }

    public static function error($message, $code) {
        return self::response(null, $message, $code);
    }

    public static function error400($message) {
        return self::response(null, $message, Response::HTTP_BAD_REQUEST);
    }

    public static function error401($message) {
        return self::response(null, $message, Response::HTTP_UNAUTHORIZED);
    }

    public static function error500($message) {
        return self::response(null, $message, Response::HTTP_INTERNAL_SERVER_ERROR);
    }
}

app\Traits\ResourceTrait.php

<?php

namespace App\Traits;

use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use InvalidArgumentException;

trait ResourceTrait
{
    /**
     * 处理分页数据并格式化
     * @param LengthAwarePaginator $paginator
     * @param callable|string $func 格式化单个项的方法名或回调函数
     * @return array
     */
    public function paginator(LengthAwarePaginator $paginator, $func = 'item'): array
    {
        return [
            'total' => $paginator->total(),
            'items' => $this->formatItems($paginator->items(), $func),
        ];
    }

    /**
     * 处理集合数据并格式化
     * @param Collection $items
     * @param callable|string $func 格式化单个项的方法名或回调函数
     * @return array
     */
    public function data(Collection $items, $func = 'item'): array
    {
        return $this->formatItems($items->all(), $func);
    }

    /**
     * 默认的单个数据项格式化逻辑(可被子类重写)
     * @param mixed $item
     * @return mixed|null
     */
    public function item($item)
    {
        return $item ?? null;
    }

    /**
     * 批量格式化数据项
     * @param array $items 要格式化的数据项数组
     * @param callable|string $func 格式化单个项的方法名或回调函数
     * @return array
     * @throws InvalidArgumentException
     */
    private function formatItems(array $items, $func): array
    {
        // 检查方法是否存在(当$func是方法名时)
        if (is_string($func) && !method_exists($this, $func)) {
            throw new InvalidArgumentException("Method {$func} does not exist in " . static::class);
        }

        // 使用集合map方法格式化,更简洁
        return collect($items)->map(function ($item) use ($func) {
            if (is_callable($func)) {
                // 修复:将当前item传递给回调函数
                return $func($item);
            }
            // 调用当前类的方法格式化
            return $this->{$func}($item);
        })->all();
    }
}
2周前 评论

有data时:code message data 没有 code message 最简单

2周前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!