这里就不去叙说dingo/api的牛逼之处了,要是不知道你也不会看这篇文章。
集成dingo/api
1.安装扩展包
命令行cd到laravel项目安装目录,执行下面命令安装dingo/api扩展包:
1
|
composer
require
dingo
/
api
:
1.0
.
x
@
dev
|
还可以直接修改composer.json文件中的require字段,增加下面键值对:
1
|
"dingo/api"
:
"1.0.*@dev"
|
并在laravel项目安装目录下执行下面命令:
1
|
composer
update
|
2.注册服务提供者
在config/app.php注册到 providers 数组:
1
2
3
4
|
'providers'
=
>
[
//...
Dingo
\
Api
\
Provider
\
LaravelServiceProvider
::
class
,
]
|
3.生成微调配置文件
如果需要对dingo/api配置进行微调,可以执行下面命令,生成配置文件到config/api.php:
1
|
php
artisan
vendor
:
publish
--
provider
=
"Dingo\Api\Provider\LaravelServiceProvider"
|
4.配置
在.env配置文件中,设置dingo/api相关配置:
1
2
3
4
5
6
7
8
9
10
|
API_STANDARDS_TREE
=
vnd
// 环境
API_SUBTYPE
=
myapp
// 子类型
API_PREFIX
=
api
// 前缀
API_DOMAIN
=
api
.
myapp
.
com
//子域名 (前缀和子域名只能存在一个)
API_VERSION
=
v1
// 版本
API_NAME
=
My
API
// 名字(使用API Blueprint命令才会用到)
API_CONDITIONAL_REQUEST
=
false
// 带条件的请求
API_STRICT
=
false
// Strict模式
API_DEFAULT_FORMAT
=
json
// 响应格式
API_DEBUG
=
true
// 调试模式
|
至此,dingo/api集成就完成了,dingo/api的使用,将在后面慢慢列出。
集成jwt
jwt即JSON Web Token的缩写,是一种api身份认证的方式。相比session,session是基于cookie的,而app则不方便处理cookie。
1.安装扩展包
命令行cd到laravel项目安装目录,执行下面命令安装jwt扩展包:
1
|
composer
require
tymon
/
jwt
-
auth
0.5
.
x
|
还可以直接修改composer.json文件中的require字段,增加下面键值对:
1
|
"tymon/jwt-auth"
:
"0.5.*"
|
并在laravel项目安装目录下执行下面命令:
1
|
composer
update
|
2.注册服务提供者
和dingo/api一样,在config/app.php注册到 providers 数组:
1
2
3
4
|
'providers'
=
>
[
//...
Tymon
\
JWTAuth
\
Providers
\
JWTAuthServiceProvider
::
class
,
]
|
3.注册门面
在config/app.php中注册门面,也是也就是取别名而已:
1
2
3
4
5
|
'aliases'
=
>
[
// ...
'JWTAuth'
=
>
Tymon
\
JWTAuth
\
Facades
\
JWTAuth
::
class
,
'JWTFactory'
=
>
Tymon
\
JWTAuth
\
Facades
\
JWTFactory
::
class
,
]
|
4.生成微调配置文件
和dingo/api一样,如果需要对jwt配置进行微调,可以执行下面命令,生成配置文件到config/jwt.php:
1
|
php
artisan
vendor
:
publish
--
provider
=
"Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
|
5.生成秘钥
执行下面命令,会在jwt.php文件中数组的secret键成对应的值:
1
|
php
artisan
jwt
:
generate
|
6.配置
jwt.php配置详解如下:
- ttl:token有效期(分钟)
- refresh_ttl:刷新token时间(分钟)
- algo:token签名算法
- user:指向User模型的命名空间路径
- identifier:用于从token的sub中获取用户
- require_claims:必须出现在token的payload中的选项,否则会抛出TokenInvalidException异常
- blacklist_enabled:如果该选项被设置为false,那么我们将不能废止token,即使我们刷新了token,前一个token仍然有效
- providers:完成各种任务的具体实现,如果需要的话你可以重写他们
User —— providers.user:基于sub获取用户的实现
JWT —— providers.jwt:加密/解密token
Authentication —— providers.auth:通过证书/ID获取认证用户
Storage —— providers.storage:存储token直到它们失效
至此,jwt集成就完成了。
dingo/api路由配置
路由在dingo/api中被称为Endpoint,便于区分web路由和api路由,也方便api版本和其他访问限制管理。
开发接口和开发web也一样,首先在app/Http/routes.php中配置Endpoint,下面来看一段简单的Endpoint配置:
1
2
3
4
5
6
7
8
9
10
11
|
// 接管路由
$
api
=
app
(
'Dingo\Api\Routing\Router'
)
;
// 配置api版本和路由
$
api
->
version
(
'v1'
,
[
'namespace'
=
>
'App\Http\Api\V1\Controllers'
]
,
function
(
$
api
)
{
// 授权组
$
api
->
group
(
[
'prefix'
=
>
'auth'
]
,
function
(
$
api
)
{
$
api
->
post
(
'register'
,
'AuthenticateController@register'
)
->
name
(
'auth.register'
)
;
}
)
;
}
)
;
|
和web路由组配置类似,除了可以在version中配置版本号以外,也可以在后面一个参数中配置路由组的命名空间、前缀、中间件等,并且支持resources路由、嵌套路由,和路由别名。
根据上面的配置,命名空间在App\Http\Api\V1\Controllers下,这样做是为了和web逻辑分离,并且便于api版本管理。
dingo/api响应
下面来看一下auth.register路由对应控制器的方法实现:
{
$rules = [
'name' => ['required'],
'phone' => ['required', 'min:11', 'max:11', 'unique:users'],
'password' => ['required', 'min:6', 'max:16'],
'key' => ['required', 'min:6'], // 手机验证码
];
$payload = $request->only('name', 'phone', 'password', 'key');
$validator = Validator::make($payload, $rules);
// 验证手机验证码
if (Cache::has($payload['phone'])) {
$key = Cache::get($payload['key']);
if ($key != $payload['key']) {
return $this->response->array(['error' => '验证码错误']);
}
} else {
return $this->response->array(['error' => '验证码错误']);
}
// 验证格式
if ($validator->fails()) {
return $this->response->array(['error' => $validator->errors()]);
}
// 创建用户
$result = Users::create([
'name' => $payload['name'],
'phone' => $payload['phone'],
'password' => bcrypt($payload['password']),
]);
if ($result) {
return $this->response->array(['success' => '创建用户成功']);
} else {
return $this->response->array(['error' => '创建用户失败']);
}
}
和web端开发比较可以很容易发现有一个不同点,return的不再是一个view,而是使用$this->response属性的array方法来创建的一个响应结果。也就是接收到一个客户端的请求并返回响应给客户端。
这里的response属性,其实是dingo/api提供的,需要在控制器里引入Dingo\Api\Routing\Helpers命名空间。为了让所有控制器都可以使用,我们可以自定义一个控制器继承系统的Controller控制器,并在我们自定义的控制器里引入命名空间,我们后续创建的控制器都继承自我们自定义的这一个基控制器:
1
2
3
4
5
6
7
8
9
10
11
|
namespace
App
\
Http
\
Api
\
V1
\
Controllers
;
use
App
\
Http
\
Controllers
\
Controller
;
use
Illuminate
\
Http
\
Request
;
use
Dingo
\
Api
\
Routing
\
Helpers
;
use
App
\
Http
\
Requests
;
class
BaseController
extends
Controller
{
use
Helpers
;
}
|
这样,BaseController的所有子控制器都能使用了。
上面的register例子中,都是响应数组,其实也就是直接响应了json。
1
|
return
$
this
->
response
->
array
(
[
]
)
;
|
除了能响应数组,dingo/api还提供了其他便捷的响应方式。比如单个item响应、集合响应、分页响应、无内容响应、创建响应和错误响应,并且可以给响应添加元数据、状态码,这些功能具体请看dingo/api官方文档。
dingo/api转化器
有些时候我们需要返回用户数据,在web端开发我们可以直接传递一个对象,但在api开发中则是需要把对象转化为标准的json格式响应给客户端。其实我们也可以自己根据对象的属性拼接一个数组,并响应给客户端,但是dingo/api给我们提供了更便捷的方式,也就是转化器(Transformer)。
自定义转化器需要继承TransformerAbstract类,并至少实现transform方法。例如下面是一个User模型的转化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
namespace
App
\
Http
\
Api
\
V1
\
Transformers
;
use
App
\
Http
\
Api
\
V1
\
Model
\
User
;
use
Illuminate
\
Http
\
Request
;
use
App
\
Http
\
Requests
;
use
League
\
Fractal
\
TransformerAbstract
;
class
UserTransformer
extends
TransformerAbstract
{
public
function
transform
(
User
$
user
)
{
return
[
'id'
=
>
$
user
->
id
,
'name'
=
>
$
user
->
name
,
'email'
=
>
$
user
->
email
,
'phone'
=
>
$
user
->
phone
,
]
;
}
}
|
transform将User对象转成了一个数组返回。
再来看看如何使用自定义的转化器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
namespace
App
\
Http
\
Api
\
V1
\
Controllers
;
use
App
\
Http
\
Api
\
V1
\
Model
\
User
;
use
App
\
Http
\
Api
\
V1
\
Transformers
\
UserTransformer
;
use
Illuminate
\
Http
\
Request
;
use
App
\
Http
\
Requests
;
class
UserController
extends
BaseController
{
public
function
getUserInfo
(
$
id
)
{
$
user
=
User
::
findOrFail
(
$
id
)
;
return
$
this
->
response
->
item
(
$
user
,
new
UserTransformer
)
;
}
}
|
这里是响应单个item,传入需要转化的模型和转化器对象即可。
然后我们配置如下路由:
1
2
3
4
5
6
7
|
// 接管路由
$
api
=
app
(
'Dingo\Api\Routing\Router'
)
;
// 配置api版本和路由
$
api
->
version
(
'v1'
,
[
'namespace'
=
>
'App\Http\Api\V1\Controllers'
]
,
function
(
$
api
)
{
$
api
->
get
(
'users/{id}'
,
'UserController@getUserInfo'
)
->
name
(
'getUserInfo'
)
;
}
)
;
|
然后请求下面的接口:
1
|
yourdomains
/
api
/
users
/
1
|
得到响应结果:
1
2
3
4
5
6
7
8
|
{
"data"
:
{
"id"
:
1
,
"name"
:
"admin"
,
"email"
:
"admin@6ag.cn"
,
"phone"
:
"13666655555"
}
}
|
jwt的简单使用
jwt在api的应用一般是用于验证用户的登录有效性,比如在登录的时候,返回给用户一个token值,这个token值包含在有效期内用户才可以进行操作,token失效后,登录也就失效。
下面例子中就是在登录成功后生成一个token,关于token的有效期和一些其他配置,在config/jwt.php中可以进行设置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
function
authenticate
(
Request
$
request
)
{
$
payload
=
$
request
->
only
(
'phone'
,
'password'
)
;
try
{
if
(
!
$
token
=
JWTAuth
::
attempt
(
$
payload
)
)
{
return
$
this
->
response
->
array
(
[
'error'
=
>
'token已经失效'
]
)
;
}
else
{
return
$
this
->
response
->
array
(
[
'token'
=
>
$
token
]
)
;
}
}
catch
(
JWTException
$
e
)
{
return
$
this
->
response
->
array
(
[
'error'
=
>
'不能创建token'
]
)
;
}
}
|
刷新token
1
2
3
4
5
|
public
function
updateToken
(
)
{
$
token
=
JWTAuth
::
refresh
(
)
;
return
$
this
->
response
->
array
(
[
'token'
=
>
$
token
]
)
;
}
|
一般我的写法是在登录验证成功后生成token字符串、token有效期和用户信息一起返回给客户端,这个具体看自己的心情吧。至此,dingo/api + jwt 开发api简单流程也就介绍完了,我本人接触laravel也不久,如有不对的地方,还望大神指正。