目前在用 Laravel 做一个 APP 后台的开发。因为都是 API,所以需要定义个通用的通信协议。这个比较好解决,而且我之前的思路也都有,具体可以看这篇:设计自认为优雅的接口。这里就不再废话了。
考虑到之后的通用性和可维护性,我们决定使用 Dingo/Api 这个插件进行 API 的管理,开发。所以,理所当然的遇到了这么一个应用场景。
这是使用 Dingo/Api 之后,标准的返回值:
url: http://www.ehs.com/api/test/index
{
"status_code": 100,
"message": "成功",
"timestamp": 1457267879,
"data": "eyJmZmYiOiJmZmYifQ==",
"sign": "d550edf4061cd60a404cd18350a1dbf299e4e69502c5be64de58861565c8e9ea"
}
但是,如果代码中出现了错误,或者说异常吧。常见的就是使用了未定义的方法,或者什么乱七八糟的错误。那么,Dingo/Api 会接管所有的异常,统一返回一个通用 json,以下是开了 API_DEBUG 之后遇到的返回情况:
url: http://www.ehs.com/api/test/index
{
"message": "Undefined variable: code2",
"status_code": 500,
"debug": {
"line": 38,
"file": "/Users/Mike/Workspace/project/ehs/ehs-web/bootstrap/function.php",
"class": "ErrorException",
"trace": [
"#0 /Users/Mike/Workspace/project/ehs/ehs-web/bootstrap/function.php(38): ..."
"#1 /Users/Mike/Workspace/project/ehs/ehs-web/app/Http/..."
"#2 [internal function]: App\\Http\\Controllers\\Api\\V1\\Test\\TestController->index()",
"#3 /Users/Mike/Workspace/project/ehs/ehs-web/vendor/laravel...",
...
"#57 /Users/Mike/Workspace/project/ehs/ehs-web/public/index.php(58) ...",
"#58 {main}"
]
}
}
当然,如果线上的话,关闭 API_DEBUG , 就是这样的返回值:
url: http://www.ehs.com/api/test/index
{
"message": "Undefined variable: code2",
"status_code": 500
}
这样看上去没什么问题。因为这边 status_code 是比较规范的。对于 PHP 来说,直接 json_decode 之后,并没有什么难办的地方。但是对面安卓和 IOS 则是使用的强类型语言。尤其是 Java,需要对每一个 Json 对象进行新建,然后序列化。所以,这种格式不统一的返回结果,是无法接受的。
那么问题来了,如何修改 Dingo/Api 的错误返回值。首先,依旧是上stackoverflow,并没有什么实质性的进展,然后就是世界上最大的同性交友网站:GITHUB,果不其然,找到了同样的业务场景的提问,然后回答却是一盆冷水。作者对该业务场景的回答, 还有这个给我启发的 issue。也是蛋疼。
所以就是需要自己 Hack 了。
当然,通过跟踪代码,可以很快的找到 Dingo/Api 中的错误处理代码,但是,并不建议直接修改这边的代码,因为,之后可能还是会通过 composer 来更新项目的依赖关系,如果直接修改,那么在之后的维护中,就会有不确定因素。增加了系统的风险。所以放弃。
解决方法也很简单。首先就是需要把所有的异常信息归总到一个地方。这个可以通过很贱的配置就可以达到。我的做法就是在routes.php里面加上这么一段代码,接管所有的 Exception:
// 将所有的 Exception 全部交给 App\Exceptions\Handler 来处理
app('api.exception')->register(function (Exception $exception) {
$request = Illuminate\Http\Request::capture();
return app('App\Exceptions\Handler')->render($request, $exception);
});
然后就是 Handler 中进行判断了。首先就是找到 API 接口的 request,和正常访问的 request 的区别,这个很简单,dd 一下,就知道了。这有坑,建议不要直接 dd($request),如果电脑好,那也无所谓。
上结论就是可以通过判断如下代码进行判断:
public function render($request, Exception $e)
{
if ($request->server->get('API_PREFIX')) {
return APIReturn(ERROR, ' '.$e->getMessage(), ['code' => $e->getCode()]);
}
return parent::render($request, $e);
}
这样,当 API 访问出现异常的时候,就是如下的返回值:
url: http://www.ehs.com/api/test/index
{
"status_code": 200,
"message": "Fatal error: Call to undefined function App\\Http\\Controllers\\Api\\V1\\Test\\ddd()",
"timestamp": 1457270046,
"data": "eyJjb2RlIjowfQ==",
"sign": "8aef6cbdbc84a570f48ccb02d89c363dbc0263468682313fa1cd873fa338b161"
}
至此,暂时解决了这个蛋疼的小问题。接下来就是 API 中最麻烦的状态维持,就是 APP 的用户登录。这个,虽然有 oath2.0这样的标准在这边,但是,可能还是会再调研几个其他的解决办法。方便开发和维护吧。尽可能的简答,是我一直想做的。越简单越好。
怎么能开发出,这个标准的API啊 我登录成功后就一个返回值 就是token值 怎么加上状态码和消息呢,求解
@小宁:问题不是很清楚。你是用的 laravel 自带的登陆吗?有使用 Dingo\Api 吗?
这个标准 API 主要还是一个规范吧,还是需要在开发中维持的。
是这样的,我现在用dinggo api和jwt开发 restapi 但是就是不标准,比如说登陆成功后,直接返回token值 而没有状态码和message 我想问您,怎么能统一返回格式
@小宁:你是希望统一 Dingo api 的返回值吧,你可以参照 https://github.com/dingo/api/wiki/Responses#custom-response-formats 这个文档,通过继承 Dingo\Api\Http\Response\Format\Format 然后修改默认的 json formatter 函数,把你需要的一些统一的东西放进去,比如 message 和 status code。
你也可以直接写一个公用函数,然后直接调用。比如:
/**
* 这个主要用于 API 方法的返回值,包括添加时间戳,和 sign
* @param $code int 使用常量 SUCCESS or ERROR
* @param $message string 返回的消息
* @param $data array 返回的内容
* @return array 用于返回的 array
*/
function APIReturn($code, $message, $data = [])
{
$res[‘status_code’] = $code;
$res[‘message’] = $message;
$res[‘timestamp’] = time();
if (empty($data) && is_array($data)) {
$data = (object)$data;
}
// $res[‘data’] = $data;
$res[‘data’] = base64_encode((json_encode($data, JSON_UNESCAPED_UNICODE)));
$res[‘sign’] = md5($res[‘data’] . $res[‘timestamp’]) . md5($res[‘timestamp’]);
return response($res, 200)->header(‘Content-Type’, ‘application/json’);
}
不知道有没有解决您的问题。
public function formatEloquentModel($model)
{
// TODO: Implement formatEloquentModel() method.
}
public function formatEloquentCollection($collection)
{
// TODO: Implement formatEloquentCollection() method.
}
public function formatArray($content)
{
// TODO: Implement formatArray() method.
}
public function getContentType()
{
// TODO: Implement getContentType() method.
}
}
这些代码您能解释一下我应该怎么实现这些抽象类才能写出同一个API 您能解释一下吗?谢谢!
怎么本地化jwt-auth / dingoapi 抛出的异常信息?
APIReturn() 这个方法是怎么来的
@是的发的:这个就是你自己的定义函数了,比如可以写成这个。
function APIReturn($value)
{
header(“Content-type: application/json”);
echo json_encode($value, JSON_UNESCAPED_UNICODE);
exit();
}
博主你好,routes.php是指哪个文件?我貌似没找到