经过多个项目在手上的编写运营, 从第一次接触api接口开发到如今也有三月了
我会简要的说一说开发之中的一些思路的处理
遵循标准化
为什么要遵循标准化,我对标准化的理解便是, 它不是必须的,但是是要做的. 它不是一个结果,但是一个过程
API的开发中,网上大多数也都充斥着说使用ResfulApi, 的确,它超棒. 前后端开发过程中, 我们遇到的最大的毛病就是沟通产生的时间比较长, 当有一套标准也是一套规范的时候, 我们需要进行合理的进行约定俗称, 减少在例如一些状态码,报错参数,请求头的一些问题
当然,这些问题并不只是为了给前端看, 同事也是为了给开发者一起看, 测试, 产品等等. 同样开发者自己必然不是独立开发, 约定俗成一套方法总是非常舒服的
所以开发准备开始阶段就必须谋划这些事情 - 这是必须的
我所使用的一些状态码信息
在HTTP请求响应时, 总是会携带一个 http状态码, 我当时是这样考虑的
每次请求返回的数据按照觉得的 http 状态码进行报错, 但试验中发现这是不合理的, 当我404 又或者500 的时候, 浏览器会直接响应它面对这些报错方法的时候应有的态度, 那就是页面不能正常浏览了
所以,我们选择在 返回的data数据中进行响应code, 如
{
"msg": "请填写正确的邮箱或者手机号码",
"code": 401,
"data": {}
}
以下是状态码的详情信息
状态值 | 说明 | 详细信息 | |
---|---|---|---|
200 | 获取成功 | (会携带数据 data) | |
201 | 获取成功 | 不携带数据 data 为空 | |
401 | 参数校验失败 | 例如 表单需要 4 个参数 , 但是只给2个参数 | |
403 | 授权失败 | 例如密码错误, 验证码错误 | |
404 | 模型未找到 | 例如 用户未找到, 文章未找到 | |
500 | 创建失败 | 例如读取文章发生错误 , 创建用户发生错误 |
目录结构
目录,文件或许大家在使用中会进行忽略,而采用自己最舒服的方式进行开发. 我因为使用laravel挺多, 参考的源码包括一些使用的经验整理出来
API 开发 =
MC -> MC+SERVER
应用与应用之间的隔离, 模块与模块之间的隔离, 不要过分追求耦合度, 也不要太过隔离
我的使用
admin(后台) - index(前台) 各自使用各自的控制器,禁止之间相互调用
添加common目录, common中添加server, traits, validate, 基类的model 基类的controller
如图 :
这里的士例采用的是laravel框架
基础模型的使用
在开发过程中, 我们必须又或者经常使用到的应该有几样
1) 分页
2) 查询过滤
3) 获取器
4) 修改器
5) 类型转换
我们挨个来讲
分页:
我采用查询作用域的方式进行, 子类的model全部继承于BaseModel 使用方式也类似于预加载方式
namespace app\index\model;
class BaseModel extends BaseModelCommon
{
/**
* app分页
* @param $query
* @return mixed
*/
public function scopePaged($query)
{
$request = request();
$page = $request->param('page',\config('api.page'));
$pagesize = $request->param('pagesize',\config('api.pagesize'));
$query = $query->page($page,$pagesize);
return $query;
}
....
使用方式
use app\index\model\MessageModelUser;
use think\Request;
class Message extends BaseController
{
$messages = $this->user->messages()->paged()->where('readed', $readed)->select();
....
注意: 这里的$this->user->messages()
是我获取到的一个关联模型, 所以它能访问到模型方法
查询过滤:
同样, 使用作用域的方式进行, 原因在于,查询的方式变化极大, 随时可能改变需求, 同时有些查询方式可以进行耦合, 例如排序, 时间范围查询, 这些都可以定义成一个模块来进行,类似于上面的分页
代码展示:
/**
* 自定义查询条件
* @param $query
* @param array $arrs
* @return mixed
*/
protected function scopeCondition($query)
{
$request = Request::instance();
$type = $request->param('type', 'desc');
$create_time = $request->param('create_time', 'desc');
$query->order("type {$type}");
$query->order("create_time {$create_time}");
$username = $request->param('username', '');
$comment = $request->param('comment', '');
if( $username ) {
$query->with(['user' => function($query) use ($username) {
$query->where('user_nickname', 'like', "%{$username}%");
}]);
}
if( $comment ) {
$query->where('comment', 'like', "%{$comment}%");
}
return $query;
}
获取器 |修改器
这个方法非常喜爱呀, 大家可以去查看文档,结合 $append 来进行使用更秒
....
protected $append = [
"readed",
"send_timed"
];
...
public function getReadedAttr($value, $data)
{
return MessageModelUser::where('message_id', $data['id'])->where('user_id', $GLOBALS['user_id'])->find()->readed;
}
...
会自动写入到模型中去
类型转换
protected $type = [
'send_time' => 'timestamp'
];
控制器方面的使用
开发过程中, 必然而然的必须返回一个指定的状态数据给前端, 这个时候便如上所说的必须规范我们的响应的接口了
这里我使用的是我在借鉴的基础上自己改写的一些方法
ApiResponse.php
相关代码展示
namespace app\common\Traits;
use think\exception\HttpResponseException;
use think\response\Json as JsonResponse;
.....
/**
* @param $data
* @param array $header
* @return mixed
*/
public function respond($data, $header = [])
{
$type = request()->isAjax() ? 'json' : "html";
$response = JsonResponse::create($data, $type, $this->code, $header);
if( $this->debug ) {
return $response; # response
}
throw new HttpResponseException($response);
}
...
/**
* 获取成功调用的方法
* @param $data
* @param string $status
* @return mixed
*/
public function successed($data, $message = "success")
{
$this->status($message, compact('data'), 200, 200);
}
适当的讲解一下, 这里的错误返回, 不是直接的return, 不适用return的原因在于, 框架会自带许多的响应请求, 包括有时候我们想抛出错误的时候例如在模型中需要不断的进行返回
这里采用的是抛出Json响应错误的方式进行, 首先创建一个JsonResponse响应, 再使用 HttpResponseException
抛出这个错误响应, 直接断开了下面的响应方式
这里采用的是 Trait
方式进行, 使用的时候直接 use 即可, 不过建议这个方式写入在基类控制器中
错误的处理
/**
* 异常处理接管, debug关闭状态下防止抛出500错误
* 以下情况将会导致500错误抛出
* 致命错误 | 系统严重错误 | 代码的严重问题
* Class Handler
* @package app\common\exception
*/
class Handler extends Handle
{
use ApiResponse;
// 重新render方法
public function render(Exception $e)
{
try {
$isDebug = config('app.app_debug'); # 判断是否是断点模式
$this->debug = true;
# 走系统抛出的系统
if( !request()->isAjax() || $isDebug) {
return parent::render($e);
}
# 错误的信息, 用于写入日志
$error_info = [
'code' => $e->getCode(), # 错误代码描述
'line' => $e->getLine(), # 错误代码行
'message' => $e->getMessage(), # 错误详细信息
'file' => $e->getFile() # 错误文件名称
];
# findOrFail 使用
if( $e instanceof ModelNotFoundException) {
return $this->status("未找到", [], 404, 5000);
}
# 写入日志
LogModel::ILogObserver("系统异常: ", $error_info);
# 这里自定义异常
return $this->status($e->getMessage(), compact('error_info'), 404, 5000);
}catch (\Exception $exception) {
return parent::render($exception);
}
}
简单的解释一下, 自定义错误接管, 当系统抛出一个非正常的错误后, 会被此类进行接受.
检查当前是否是开发模式或者是否是ajax请求, 是的话则走系统抛出的错误
同时写入日志
完成错误接受后, 返回一个自定义的错误返回, 最大程度的杜绝掉一些未知的bug, 并且友好的显示前端, 同时针对错误日志进行分析debug
验证器的使用
参考我上次写的文章
系列1 结束, 下次再来!
来自 邓尘锋的博客
本文由 邓尘锋 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: May 9, 2019 at 04:23 pm