Laravel 开发 RESTful API 的一些心得

最近用 Laravel 写了一段时间的 API,总结一下自己的心得吧。

Start#

  • API 开发我们可以看到,有些网站用 token 验证身份,有些用 OAuth2.0,当时我也纠结,然后看到一个不错的说法。大方面,会涉及到给别人用的使用 OAuth,自己使用的用 token 就足够了
  • 设计最初,最好在路由加个版本号,方便以后扩展
    Route::prefix('v1')->group(function () {
    // more
    });
  • 如果前端想跨域,请使用这个很方便的包 barryvdh/laravel-cors

    一个简单的接口示例
    api实例输出

    验证#

  • API 开发总会离不开验证,这里推荐使用 jwt-auth,1.0 快要来了,新版本的文档也很清晰
  • 刚用 jwt-auth 时有疑问,Laravel 自带的 token 验证使用的是数据库 api_token 字段验证,而不见 jwt-auth 需要这个
    • 然后想自己看源码,结果 QAQ
    • 最后去问了官方 >_<
    • 原来用户的信息已经存储在 token 中加密
    • 一开始有疑问,这样保存,不会被解密吗 (真为自己智商担忧!_!)
    • 后来才想起,jwt 一开始就运行 php artisan jwt:secret 生成了秘钥
    • 你不泄露就保证安全了~~~

      路由#

  • 当然使用官方 api 的路由 Route::apiResource(), 一条更比五条强
  • 路由的名字当然是 RESTful 的方式
  • 保持动词,复数形式,见名知义
  • 有些长的路由,应该用什么分隔呢?
  • laravel 用的是中划线 (-),因为谷歌收录时,按中划线划分关键字,国内的是按下划线 (_) 收录,具体看自己了,我是喜欢下划线 >_<
  • 更多看这里: 路由命名规范

    表单验证#

    可以使用控制器自带的表单验证,更推荐使用 表单类 , 能分离都分离出去,控制器不要处理太多事情。
    表单验证
    能分离的代码都不要吝啬~~~

    数据转换#

  • Laravel 自带的 API Resource
  • 用起来真的很方便,不过发现一个问题,--collection 的格式总是转不过来,后来直接放弃了
  • 单个的使用 Resources
  • 集合的使用 Resources::collection() 发现,特别好用 >_<
  • 不得不说,多对多关联时,Laravel 处理得太好了条件关联
    数据转换
  • 在上面这个例子中,如果关联没有被加载,则 posts 键将会在资源响应被发送给客户端之前被删除。
  • 在有不确定是否输出关联数据时,这是一个很有用的功能!!!

    响应输出#

    当时在 laravel-china 看到的这个帖子,然后觉得这个方式不错,所以自己也这样子,使用基类的方法统一响应输出。

    异常#

    异常算是一大手笔了,处理好异常,可以让你的代码优雅很多。
    \App\Exceptions\Handler::render 方法可以捕获到很多有用的异常,例如,我的代码是这样写的:
    异常捕获
    UnauthorizedHttpException 这个是捕获 jwt 异常
    ValidationException 这个是表单异常,捕获之后,表单错误消息可以很好的格式化,
    ModelNotFoundException 这个是模型找不到的异常,捕获之后,可以直接在控制器直接这样

// 未捕获之前的写法
public function show($id)
{
    $user = User::find($id);
    if (! $user) {

    }

    // do something
}

// 现在
public function show($id)
{
    $user = User::findOrFail($id);
}
// 甚至这样
public function show(User $user)
{
    // do something
}
  • 下面这两个异常可以不捕获,只是方便开发中查看错误消息
    NotFoundHttpException404 路由找不到的异常,没什么好说的了
    MethodNotAllowedHttpException 这个是方法不对应,比如你是 get 路由,却 post 请求

    文档#

  • 差点忘了这个,文档非常非常重要
  • 我是不怎么喜欢在注释写文档的
  • 使用 swagger-ui+swagger-edit
    • 下载 swagger-ui
    • 只需要 dist 目录的东西(其他可以删除了)
    • 下载 swagger-editor
    • 只要 dist 目录的东西和根目录的 index.html
    • 我还把 swagger-editorindex.html 改成了 edit.html,然后把这两个东西整合到同一个目录(记得修改 css,js 的位置)
    • 新建两个文件 api.json,api.yaml 大概就和图中差不多
    • 要修改图中箭头所示成为 api.json 的位置
      api
  • 访问 edit.html 可以书写文档
  • 访问 index.html 可以查看文档
  • edit.html 写好之后,导出 json,然后粘贴到 api.json 文件
    api
  • 记得也把写好的格式保存到 api.yaml,因为清楚缓存之后,下次访问时会消失

    自己写了一个 packages#

  • 就方便创建控制器,验证
  • 所有控制器继承重写过的基类,响应输出方便。
  • 例如完整验证只需要三秒钟
    • 第一秒: php artisan api:auth
    • 第二秒:出现图代表成功;
      laravel-api-helper
    • 第三秒:拿出手臂的劳力士,确定只过了三秒
      手臂的手表
  • 更多的使用:laravel-api-helper

    工作和 API 开发有关,用到其他有经验了再回来补补。

    更多参考#

    RESTful API 设计指南

本作品采用《CC 协议》,转载必须注明作者和本文链接
当神不再是我们的信仰,那么信仰自己吧,努力让自己变好,不辜负自己的信仰!
本帖由 Summer 于 7年前 加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 26
Aaron

我建议统一响应类的设计,最好用实现 responsable 接口,好一点。

7年前 评论

jwt 的 token 本身是明文的,key 只用来签名
jws 才是加密.

7年前 评论

@Aaron 因为有一个状态类继承了 response 对象,响应用 laravel 的方便

7年前 评论

@symoo jwt 应该并不是明文 的,因为有一个 JWT_SECRET 来使信息加密了。

7年前 评论

jwt-auth 有时间的朋友去研究一下吧,刚才看了源码,有点心酸,转来转去,最后用了个 sha256 签名,然后又用了 hash 加密,头晕,有看得透彻的朋友可以说一下 >_

7年前 评论

@DavidNineRoc jwtauth 说白了就是把 user id 用后端定义的 jwt-secret 加密了,响应的 token 里有 base64 加密的其他配置比如加密方法,说的再透彻一点就是把用户 id 存到了 header 的 Authorization 中,服务端也会缓存这个 token

7年前 评论

跨域,用 包barryvdh/laravel-cors 不就是一句 Access-Control-Allow-Origin: * 就可以了 为啥还要搞的那么麻烦

7年前 评论

@datou 这样写是行呀,然后你想全局使用,得新建一个中间件,然后你又想配置只允许某些域名跨域,又得加一个配置文件。
这些时间足够你运行 composer update:smile:

7年前 评论

jwt 并不会加密 user_id,user_id 和 其他 payload 都是明文 base64 的。不过后面会有一个通过 JWT_SECRET 和用户信息 payload 生成的串来验证 你的 user_id 和其他 payload 被没被串改

7年前 评论

jwt 中间会用 '.' 来连接,你可以看下中间那段直接 base64 解密一下你的用户 ID 就出来了,但是你想随便改个用户 ID 放回去,你就会发现验证不通过了

7年前 评论

@hooklife 受教了,也看到了 jwt-auth 的有一个 issure 也说了这个事情

7年前 评论

个人觉得还是用自定义的状态码好点,毕竟 http 提供的状态码并不能根据业务去提示。

6年前 评论

@Hatcher 这个状态码当然是自定义的,暴露的 setCode 方法就是为了设置状态码的

6年前 评论
巴啦啦

唉,现在 jwt 支持多用户表吗?

6年前 评论

@仰望 支持的,切换不同守卫即可。

6年前 评论
wenber

@symoo jws 没有接触过。得去查找下

6年前 评论
ThinkQ

很好,收获。

6年前 评论
张浩浩浩浩

有一个非常严重的问题,需要问一下作者:文章中 表单验证 这一块里面的 message() 方法是提示错误语义信息的,所有如果在用 postman 的时候用这种方法的话,就会被 back() 回去,弹不出错误信息,应该怎么让后台 api 调试变得也方便呢?

6年前 评论

@张浩浩浩浩 这个异常捕获不会因为是不是 postman 而特殊,都会返回 json 响应。你的描述我也听得不是很明白。

6年前 评论
张浩浩浩浩

@DavidNineRoc 那我就简化一下我的问题: Request 里的 extends 继承类是:FormRequest ,这里面的源码有一个方法:

protected function failedValidation(Validator $validator)
    {
        throw (new ValidationException($validator))
                    ->errorBag($this->errorBag)
                    ->redirectTo($this->getRedirectUrl());
    }

这里面 很明显 有一个重定向 ->redirectTo($this->getRedirectUrl()) ,如果表单验证走了 error 之后,它会 back() 回去,但是如果 Api 接口, 我们都用 postman 没法打印出具体是什么错误,只会显示:
https://cdn.learnku.com/uploads/image...

6年前 评论

@张浩浩浩浩

file
捕获异常这一步做了吗,如果写了异常,表单发生错误可以在这里返回 json

6年前 评论
张浩浩浩浩

@DavidNineRoc 谢谢博主的耐心解答,解决了我的大问题。

/**
     * 重写 - 将异常呈现为HTTP响应 (Render an exception into an HTTP response)
     *
     * @param \Illuminate\Http\Request $request
     * @param Exception $exception
     * @return \Symfony\Component\HttpFoundation\Response
     * @author SuperHao - 619596123@qq.com
     */
    public function render($request, Exception $exception)
    {
        if ($exception instanceof ValidationException) {
            // 捕获 request
            return $this->response->error($exception->validator->errors()->first());
        }

        /**
         * 没有捕获到走源码
         */
        return parent::render($request, $exception);
    }

创建绑定了一个契约接口

/**
         * singleton单例绑定
         *
         * @param  \App\Contracts\Response        契约接口类
         * @param  \App\Services\ResponseService  接口实现类
         */
        $this->app->singleton(ResponseContracts::class, ResponseService::class);
        /**
         * 起别名
         *
         * @param  \App\Contracts\Response  契约接口类
         * @param  string $name
         */
        $this->app->alias(ResponseContracts::class, 'MResponse');

然后实现接口 json 返回数据:

{
    "status": false,
    "code": 404,
    "message": "用户名已存在,请前去登陆",
    "data": []
}
6年前 评论

未填写
文章
42
粉丝
158
喜欢
713
收藏
347
排名:30
访问:22.2 万
私信
所有博文
社区赞助商