这里就不去叙说dingo/api的牛逼之处了,要是不知道你也不会看这篇文章。
集成dingo/api
1.安装扩展包
命令行cd到laravel项目安装目录,执行下面命令安装dingo/api扩展包:
composer require dingo/api:1.0.x@dev
还可以直接修改composer.json文件中的require字段,增加下面键值对:
"dingo/api": "1.0.*@dev"
并在laravel项目安装目录下执行下面命令:
composer update
2.注册服务提供者
在config/app.php注册到 providers 数组:
'providers' => [ //... Dingo\Api\Provider\LaravelServiceProvider::class, ]
3.生成微调配置文件
如果需要对dingo/api配置进行微调,可以执行下面命令,生成配置文件到config/api.php:
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"
4.配置
在.env配置文件中,设置dingo/api相关配置:
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扩展包:
composer require tymon/jwt-auth 0.5.x
还可以直接修改composer.json文件中的require字段,增加下面键值对:
"tymon/jwt-auth": "0.5.*"
并在laravel项目安装目录下执行下面命令:
composer update
2.注册服务提供者
和dingo/api一样,在config/app.php注册到 providers 数组:
'providers' => [ //... Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class, ]
3.注册门面
在config/app.php中注册门面,也是也就是取别名而已:
'aliases' => [ // ... 'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class, 'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class, ]
4.生成微调配置文件
和dingo/api一样,如果需要对jwt配置进行微调,可以执行下面命令,生成配置文件到config/jwt.php:
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
5.生成秘钥
执行下面命令,会在jwt.php文件中数组的secret键成对应的值:
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配置:
// 接管路由 $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路由对应控制器的方法实现:
public function register(Request $request) { $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控制器,并在我们自定义的控制器里引入命名空间,我们后续创建的控制器都继承自我们自定义的这一个基控制器:
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。
return $this->response->array([]);
除了能响应数组,dingo/api还提供了其他便捷的响应方式。比如单个item响应、集合响应、分页响应、无内容响应、创建响应和错误响应,并且可以给响应添加元数据、状态码,这些功能具体请看dingo/api官方文档。
dingo/api转化器
有些时候我们需要返回用户数据,在web端开发我们可以直接传递一个对象,但在api开发中则是需要把对象转化为标准的json格式响应给客户端。其实我们也可以自己根据对象的属性拼接一个数组,并响应给客户端,但是dingo/api给我们提供了更便捷的方式,也就是转化器(Transformer)。
自定义转化器需要继承TransformerAbstract类,并至少实现transform方法。例如下面是一个User模型的转化:
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对象转成了一个数组返回。
再来看看如何使用自定义的转化器:
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,传入需要转化的模型和转化器对象即可。
然后我们配置如下路由:
// 接管路由 $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'); });
然后请求下面的接口:
yourdomains/api/users/1
得到响应结果:
{ "data": { "id": 1, "name": "admin", "email": "admin@6ag.cn", "phone": "13666655555" } }
jwt的简单使用
jwt在api的应用一般是用于验证用户的登录有效性,比如在登录的时候,返回给用户一个token值,这个token值包含在有效期内用户才可以进行操作,token失效后,登录也就失效。
下面例子中就是在登录成功后生成一个token,关于token的有效期和一些其他配置,在config/jwt.php中可以进行设置。
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
public function updateToken() { $token = JWTAuth::refresh(); return $this->response->array(['token' => $token]); }
一般我的写法是在登录验证成功后生成token字符串、token有效期和用户信息一起返回给客户端,这个具体看自己的心情吧。至此,dingo/api + jwt 开发api简单流程也就介绍完了,我本人接触laravel也不久,如有不对的地方,还望大神指正。