一、生成测试数据
首先建立表,建立model,建立controller。
然后使用faker自动生成数据。
database--factories--ModelFactory.php
$factory ->define(App\Lesson::class,function(Faker\Generator $faker){
return [
'title' = > $faker->sentence,
'body' =>$faker->paragraph,
'free' => $faker->boolean();
];
});
php artisan tinker
namespace App;
factory(Lesson::class,60)->create();//生成60条数据
二、初步实现API系统
1.路由:
Routes::group(['prefix' => 'api/v1'], function(){
Route::resource('lessons','LessonsController');
});
php artisan route:list;//查看路由
2.返回JSON格式
return Lesson::all();
$lesson=Lesson::findOrFail($id); return $lesson;
//return 会自动转换为JSON格式
3.在Model文件中
protected $hidden = ['title'];//禁止输出某些属性
4.直接return问题
1)暴露数据结构
2)没有错误提示
三、API字段映射
1.增加提示消息
return \Response::json([
'status'=>'success',
'status_code'=> 200,
'data' =>$lessons->toArray();
]);
2.防止暴露数据库表结构
'data' => $this->transform($lessons);
private function transformCollection($lessons)
{
return array_map([$this,'transform'],$lessons->toArray());
}
private function transform($lesson)
{
return[
'title' =>$lesson['title'],
'content' => $lesson['body'],
'is_free' => (boolean)$lesson['free']
];
}
然后就可以隐藏结构,比如'body'包装成了'content'。然后返回时只要这样:
return \Response::json([
'status'=>'success',
'status_code'=> 200,
'data' =>transformCollection(Lesson::all());//index方法中
//or:
'data' =>transform(Lesson::find($id));//show方法中
]);
四、重构API代码
如果将transform和transformCollection放在Controller中,每增加一个Controller中,就需要加两个方法。。。
这种明显可以通过继承来优化,接下来看怎么做:
1.多个model的transformCollecion与transform怎么样避免重复
APP建立一个新的目录Transformer
新建Transformer.php
abstract class Transformer{
public function transformCollection($items)
{
return array_map([$this,'transform'],$items);
}
public abstract transfomer($item);
}
新建LessonTransfomer
class LessonController extends Controller{
public function transfome($lesson)
{
return[
'title' =>$lesson['title'],
'content' => $lesson['body'],
'is_free' => (boolean)$lesson['free']
];
}
}
2.LessonsController中作相应修改
class LessonsController extends Controller
{
//添加一个依赖注入
protected $lessonTransformer;
public function __construct(LessonTransformer $lessonTransformer)
{
$this->lessonTransformer = $lessonTransformer;
}
//然后在本函数中直接用,比如:
$this->lessonTransformer->transfomerCollection($lessons);
}
composer dump-autoload//自动加载文件重新编译,重新加载
五、控制返回错误信息
1.创建一个ApiController
包含一些response(),responseError()等方法
2.LessonsController 继承ApiController
class ApiController extends Controller
{
protected $statuscode=200;
phpstorm可以快捷创建setter,getter
public function getStatusCode(){
return $this->statuscode;
}
public function setStatusCode($code){
$this->statuscode=$code;
return $this;//这样在方法中可以用链式操作,比如$this->setStatusCode(404)->responseNotFound();
}
public function responseNotFound($message = 'Not Found'){
return $this->responseError($message);
};
public function responseError($message){
return $this->response([
'status' = > 'failed',
'error' => [
'status_code' = > $this->getStatusCode(),
'message'=>$message
]
]);
};
public function response($data){
return \Response::json($data,$this->getStatusCode());
};
}
3. HTTP返回码得意义:
1**:hold on 持续请求中
2**:here you go 正常返回
3**:go away重定向
4**: you fucked up 客户端请求故障
5**: I fucked up 服务器端故障
六、对请求API的用户认证
认证的引出——对于POST请求,为安全性我们不能允许响应所有人的请求,因此要作认证。
用google的POSTMAN来进行POST请求,以供测试
注意到提交POST时,WEB用FORM表单提交POST,API测试可以用POSTMAN
Routes::resource方法生成的post操作对应的是store方法。
注释掉Http下的Kernel.php中的$middleware \App\Http\Middleware\VerifyCsrfToken::class,才可以相应Post
三种认证方法:baseAuth,Auth2.0, JWT.
这里先介绍baseAuth
1.在Controller的构造函数中:
$this->middleware('auth.basic',['only'=>['store','update']]);//即指明store方法需要验证,默认以User表为准
2.在store函数中:
public function store(Request)
{
if(!$request->get('title') or !$request->get('body')){
return $this->setStatucCode(422)->responseError('validate fails');
}
Lesson::create($request->all());//不要忘记去Model文件中修改权限$fillable
return $this->setStatusCode(201)->response([
'status'=>'success',
'message'=>'lesson created'
]);
}
七、安装DingoAPI和jwt-auth
两个项目在github中直接搜即可
1.通过composer安装DingoAPI,在require中添加
"require":{
"dingo/api": "1.0.*@dev",
"tymon/jwt-auth": "0.5.*"
}
composer update
配置DingoAPI
config--app.php的provider中添加:
'provider' => [Dingo\Api\Provider\LaravelServiceProvider::class]
php artisan vendor:publish --provider=Dingo\Api\Provider\LaravelServiceProvider'
这时在config文件夹下生成了api.php
2.安装JWT-AUTH,直接看
https://github.com/tymondesigns/jwt-auth/wiki/Installation
在$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServ之后会在config文件夹下生成jwt.php
在$ php artisan jwt:generate之后会生成一个随机的Key
八、Dingo API初探
按照github的wiki中的configuration来配置即可
将之前的transfomer用dingo/API来实现。
1.基本配置在.env中
API_STANDARDS_TREE=vnd
API_PREFIX=api
API_VERSION=v1
API_DEBUG=true
2.类似于前面的transformer类,dingo/api有TransFormerAbstract这个类。
$this->collection($lessons, new AtransFormer());
九、JWT
1.建立一个api路由,register,login,利用JWT实现token,实现权限访问。
dingo/api函数:$this->item($lessons, new AtransFormer());
2.JWT用token来获得当前用户信息。
具体内容:
a.创建AuthController,定义authenticate函数,由mail和passwd返回token值
b.routes利用post访问Authcontroller的index方法,获得token
3. routesl增加middleware=》jwt.auth(在kernel文件中配置).
4.增加post路由获得register。同时在controller中增加register方法。
$user=User::create($newUser);
$token=JWTAuth::fromUser($user);
return response()->json(compact('token'));
若将password字段改名为user_password,
a.需要在AuthController中修改authenticate方法 $credentials=['user_name'=>$request->get('user_email')];
b.需要在相应的usermodel中增加:getAuthPassword(){return $this->user_password;}
jwt中有一个getAuthenticateUser()方法,是通过token来获取用户
if(! $user = JWTauth::parseToken()->authenticate()){return ...}
十、OAuth2.0
从dingo/api的autheratic中找到OAuth2.0,然后找到相应的laravel5安装和配置方法即可。
https://github.com/dingo/api/wiki/Authentication
https://github.com/dingo/api/wiki/OAuth-2.0
https://github.com/lucadegasperi/oauth2-server-laravel/wiki/Laravel-5-Installation
最后会再config下生产能Oauth2.php
使用Oauth2.0,基本配置见:
https://github.com/lucadegasperi/oauth2-server-laravel/wiki/Implementing-an-Authorization-Server-with-the-Auth-Code-Grant
注意中间会生成一堆表,建一个client model对应oauth_clients表。生成数据后,将client_id复制到oauth_client_endpoints表格中,并且填写下redirect_uri即可。
这里我们需要先在oauth_clients增加client_id,client_secret,还有oauth_client_endpoints的redirect_uri。
先用client_id, response_type=code, redirect_uri,user_id向oauth/authorize利用POST请求code(这里需要用户先登录,在路由中用Auth::user()->id获得用户id)
先利用client_id,client_secret, redirect_uri, grant_type=authorization_code, code向\oauth\token来申请access_token。然后就可以使用oauth的Middleware了。
github例子中请求全过程:(这里是以已经登录为前提)
以下以User代表用户浏览器,Client 代表应用服务器client server,Open代表开放平台接口服务器。
1.User首先向Client发出访问第三方登录界面的请求,如本例中的get oauth/authorize,
2.Client返回Open的授权页面url(Open方提供服务的url,Client端需要预存),如本例中的oauth/authorization-form
3.User通过该url向Open发出GET
注意:本例实际上模拟了Client端与Open端的操作,因为这里是假设自己是Open端,所以需要对Client端进行模拟操作,1,2,3三步是User与Client的通信。
4.Open返回授权页面,比如这里的view(oauth.authorization-form)
5.在这个地方User如果没有在Open中登录,则需要先登录,需要先登录再授权,或者登录同时授权,本例假设已经登录。在这个view中,User可以点击agree或者deny,如果agree,则发出post请求给oauth/authorize,请求code
6.Open向redirect_url返回一个含有code的url
7.User跳转到6返回的url(含有code参数)中(注意这里的redirect_url是Client设置的,由client提供服务!)
8.Client提取url中的code后可以利用post向Open请求access_token,这里是向oauth/token这个路径(实际中这个路径需要Open方预先提供给Client)
9.Open返回给Client相关信息,包括access_token, uid, expire_time等
10.Client对相关信息做处理。期间可以利用access_token获取Open的其他有权限资源。
11.Client将处理结果输出给User。
code方式详见下图,这里的图来自http://drops.wooyun.org/papers/598。