归档文章 (2011-2017)

date
2017/08/01
环境:homestead 5.3.2
版本:laravel 5.4
composer config -g repo.packagist composer https://packagist.laravel-china.org composer create-project --prefer-dist laravel/laravel api 5.4.*
App\User 移动移动到 App\Models\User 目录
<?php// App\Models\User.phpnamespace App\Models;
<?php// config\auth.php'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ],],

第1章 填充测试数据

1.1 生成数据库表

http://d.laravel-china.org/docs/5.4/migrations
创建数据库表
php artisan make:migration create_lessons_table --create=lessons
创建模型和控制器(这里将模型放到 Models 目录下)
php artisan make:controller LessonController -m Models\\Lesson
修改 migrate
<?php// database\migrations\2017_07_01_182454_create_lessons_table.phpSchema::create('lessons', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->text('body'); $table->boolean('free'); $table->timestamps();});
解决导入错误
<?php// app\Providers\AppServiceProvider.phpuse Illuminate\Support\Facades\Schema;public function boot() { Schema::defaultStringLength(191);}
生成数据表
php artisan migrate

1.2 Eloquent 模型工厂填充数据

http://d.laravel-china.org/docs/5.4/database-testing#writing-factories
修改 ModelFactory.php
<?php// database\factories\ModelFactory.php$factory->define(App\Models\Lesson::class, function (Faker\Generator $faker) { static $password; return [ 'title' => $faker->sentence, 'body' => $faker->paragraph, 'free' => $faker->boolean(), ];});
方法一:使用 tinker 生成数据
php artisan tinker >>>factory(App\Models\Lesson::class,60)->create(); >>> quit
方法二:编写 Seeders 生成数据
http://d.laravel-china.org/docs/5.4/seeding
php artisan make:seeder LessonsTableSeeder
<?php// database\seeds\LessonsTableSeeder.php public function run() { factory(App\Lesson::class, 50)->create(); }
php artisan db:seed --class=LessonsTableSeeder

第2章 初步实现 API 系统

2.1 注册路由

http://d.laravel-china.org/docs/5.4/routing
<?php// routes\api.phpRoute::group(['prefix'=>'v1'], function (){ Route::resource('lesson','LessonController');});
查看已注册的路由
# 注意这里不是 api:route php artisan route:list
notion image
查看已注册的路由

2.2 编辑控制器

http://d.laravel-china.org/docs/5.4/controllers
<?php// app\Http\Controllers\LessonController.php public function index() { return Lesson::all(); } public function show(Lesson $lesson) { // 旧版本 //return Lesson::findOrfail($id); return $lesson; }

第3章 API 字段映射

3.1 附加状态到 Response 响应

http://d.laravel-china.org/docs/5.4/responses
<?php// app\Http\Controllers\LessonController.php public function index() { return \Response::json([ 'status'=>'Success', 'status_code'=>'200', 'data'=>Lesson::all()->toArray() ]); } public function show(Lesson $lesson) { return \Response::json([ 'status'=>'Success', 'status_code'=>'200', 'data'=>$lesson->toArray() ]); }

3.2 隐藏数据库字段结构

http://php.net/manual/zh/function.array-map.php
<?php// app\Http\Controllers\LessonController.php public function index() { $lessons = Lesson::all(); return \Response::json([ 'status'=>'Success', 'status_code'=>'200', 'data'=>$this->transformCollection($lessons) ]); } public function show(Lesson $lesson) { return \Response::json([ 'status'=>'Success', 'status_code'=>'200', 'data'=>$this->transform($lesson->toArray()) ]); } public function transformCollection($lessons) { return array_map([$this, 'transform'],$lessons->toArray()); } public function transform($lessons) { return [ 'title'=>$lessons['title'], 'content'=>$lessons['body'], 'is_free'=>(boolean) $lessons['free'] ]; }

第4章 重构 API 代码

<?php// app\Transformer\LessonTransfromer.phpnamespace App\Transformer;class LessonTransfromer extends Transformer { public function transform($lessons) { return [ 'title'=>$lessons['title'], 'content'=>$lessons['body'], 'is_free'=>(boolean) $lessons['free'] ]; } }
<?php// app\Transformer\Transformer.phpnamespace App\Transformer;abstract class Transformer { public function transformCollection($items) { return array_map([$this, 'transform'],$items->toArray()); } public abstract function transform($items);}
<?php// app\Http\Controllers\LessonController.phpuse App\Transformer\LessonTransfromer;class LessonController extends Controller { protected $lessonTransfromer; // 依赖注入 public function __construct(LessonTransfromer $lessonTransfromer) { $this->lessonTransfromer = $lessonTransfromer; } public function index() { $lessons = Lesson::all(); return \Response::json([ 'status'=>'Success', 'status_code'=>'200', 'data'=>$this->lessonTransfromer->transformCollection($lessons) ]); } public function show(Lesson $lesson) { return \Response::json([ 'status'=>'Success', 'status_code'=>'200', 'data'=>$this->lessonTransfromer->transform($lesson->toArray()) ]); } }

第5章 处理错误返回

http://d.laravel-china.org/docs/5.4/errors
生成 ApiController
php artisan make:controller ApiController
<?php// App\Http\Controllers\ApiControllerclass ApiController extends Controller { protected $statusCode = 200; public function getStatusCode() { return $this->statusCode; } public function setStatusCode($statusCode) { $this->statusCode = $statusCode; return $this; } public function responseNotFound($message = 'Not Found') { return $this->setStatusCode(404)->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); } }
<?php// LessonController.phpclass LessonController extends ApiController { public function index() { $lessons = Lesson::all(); return $this->response([ 'status'=>'Success', 'status_code'=>'200', 'data'=>$this->lessonTransfromer->transformCollection($lessons) ]); } public function show(Lesson $lesson) { return $this->response([ 'status'=>'Success', 'status_code'=>'200', 'data'=>$this->lessonTransfromer->transform($lesson->toArray()) ]); } }
<?php// app\Exceptions\Handler.php use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException; public function render($request, Exception $exception) { if ($exception instanceof ModelNotFoundException) { $api = new \App\Http\Controllers\ApiController; return $api->responseNotFound('404 not found'); } return parent::render($request, $exception); }

第6章 对请求API的用户认证

生成用户验证模块
php artisan make:auth
<?php// app\Http\Controllers\Auth\RegisterController.phpuse App\Models\User;
编辑控制器
<?php// app\Http\Controllers\LessonController.php public function __construct(LessonTransfromer $lessonTransfromer) { $this->middleware('auth.basic',['only'=>['index', 'store']]); } public function store(Request $request) { if (! $request->get('title') or ! $request->get('body') or ! $request->get('free')){ return $this->setStatusCode(422)->response('validata fails'); } Lesson::create($request->all()); return $this->setStatusCode(201)->response([ 'status'=>'success', 'message'=>'lesson created' ]); }
<?php// app\Models\Lesson.phpclass Lesson extends Model { protected $fillable = ['title','body','free'];}
最后使用 postman 调试 http://api.dev/api/v1/lesson/

第7章 引入 Dingo API 和 JWT

7.1 引入 Dingo API

官方: https://github.com/dingo/api
<?php// api\composer.json"require": { "dingo/api": "1.0.*@dev"}
composer update
注册 provider
# config\app.php 'providers' => [ Dingo\Api\Provider\LaravelServiceProvider::class, ]
添加 Facades
# config\app.php 'aliases' => [ 'API' => Dingo\Api\Facade\API::class, 'DingoRoute' => Dingo\Api\Facade\Route::class, ]
发布配置文件
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"

7.2 引入 JWT

主页 https://github.com/tymondesigns/jwt-auth/
<?php// api\composer.json"require": { "tymon/jwt-auth": "0.5.*"}
composer update
注册 provider
# config\app.php 'providers' => [ Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class, ]
添加 Facades
# config\app.php 'aliases' => [ 'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class, 'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class, ]
发布配置文件
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
生成key
php artisan jwt:generate

第8章 Dingo API 初探

8.1 Dingo API 配置

https://github.com/liyu001989/dingo-api-wiki-zh/blob/master/Configuration.md
# api.dev\.env API_STANDARDS_TREE=vnd API_PREFIX=api API_VERSION=v1 API_DEBUG=true
https://github.com/liyu001989/dingo-api-wiki-zh/blob/master/Authentication.md
# config\api.php // 仅供参考 'auth' => [ 'basic' => function($app) { return new Dingo\Api\Auth\Provider\Basic($app['auth']); }, 'jwt' => function ($app){ return new Dingo\Api\Auth\Provider\JWT($app['Tymon\JWTAuth\JWTAuth']); }, ], 'auth' => [ 'basic' => 'Dingo\Api\Auth\Provider\Basic', 'jwt' => 'Dingo\Api\Auth\Provider\JWT', ],
添加路由中间件
# app\Http\Kernel.php protected $routeMiddleware = [ 'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class, 'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class, ];

8.2 Dingo API 路由

这里的路由就是 Dingo API Endpoints ,端点是路由的另一种说法,详细用法见下面文档。
https://github.com/dingo/api/wiki/Creating-API-Endpoints
请注意:v1 不能乱用,它已经在你的环境配置中定义好了。 API_VERSION=v1
<?php$api = app('Dingo\Api\Routing\Router');$api->version('v1', function ($api) { $api->group(['namespace' => 'App\Api\Controllers'], function ($api) { $api->get('lessons', 'LessonController@index'); });});
查看路由,注意区别 route:lists
php artisan api:route
地址是 /api/lessons 没有 v1 前缀,访问 http://api.dev/api/lessons/ 看到已经正常。
notion image
查看已注册的路由

8.2 Dingo API 响应

https://github.com/dingo/api/wiki/Responses
下面例子演示如何使用响应生成器 (Response Builder)

8.2.1 创建 BaseController

为了使用 Dingo\Api\Routing\Helpers trait 首先要创建基础控制器
<?php// app\Api\Controllers\BaseController.phpnamespace App\Api\Controllers;use App\Http\Controllers\Controller;use Dingo\Api\Routing\Helpers;class BaseController extends Controller { use Helpers;}

8.2.2 响应一个数组

意思是直接以数组形式返回 $lessons 原始数据
创建 LessonController 继承上面的 BaseController
<?php// app\Api\Controllers\LessonController.phpnamespace App\Api\Controllers;use App\Models\Lesson;class LessonController extends BaseController { public function index() { $lessons = Lesson::all(); return $this->response->array($lessons->toArray()); } }
http://api.dev/api/lessons

8.2.3 响应一个元素

通过实例化 LessonTransformer 来处理 $lesson 数据,然后再返回经过处理的数据。
首先,新建 LessonController
<?php// app\Api\Controllers\LessonController.phpnamespace App\Api\Controllers;use App\Api\Transformer\LessonTransformer;use App\Models\Lesson;class LessonController extends BaseController { /** * 通过实例化 LessonTransformer 来处理 $lesson 数据,然后再返回 * LessonTransformer 类必须继承 TransformerAbstract 抽象类 * 因为 TransformerAbstract 类提供了 item 和 collection 方法 * 此外 LessonTransformer 类中必须有 transform 方法 */ public function show($id) { $lesson = Lesson::findOrFail($id); return $this->response->item($lesson, new LessonTransformer); } }
接着,新建 LessonTransformer ,命名空间是 App\Api\Transformer
<?php// app\Api\Transformer\LessonTransformer.phpnamespace App\Api\Transformer;use App\Models\Lesson;use League\Fractal\TransformerAbstract;class LessonTransformer extends TransformerAbstract { public function transform(Lesson $lesson) { return [ 'title'=>$lesson['title'], 'content'=>$lesson['body'], 'is_free'=>(boolean) $lesson['free'] ]; } }

8.2.4 响应一个元素集合

collection 会自动调用 new LessonTransformer() 中的 transform 方法
意思就是上面 LessonTransformer 类中必须有 transform 方法
<?php// app\Api\Controllers\LessonController.phpnamespace App\Api\Controllers;use App\Api\Transformer\LessonTransformer;use App\Models\Lesson;class LessonController extends BaseController { public function index() { $lessons = Lesson::all(); return $this->collection($lessons,new LessonTransformer()); } }

8.2.5 响应分页

<?php// app\Api\Controllers\LessonController.phpnamespace App\Api\Controllers;use App\Api\Transformer\LessonTransformer;use App\Lesson;class LessonController extends BaseController { public function index() { $lessons = Lesson::paginate(10); return $this->paginator($lessons, new LessonTransformer); } }

第9章 开始使用 JWT

9.1 路由

<?php// routes\api.php$api = app('Dingo\Api\Routing\Router');$api->version('v2', function ($api) { $api->group(['namespace' => 'App\Api\Controllers'], function ($api) { $api->post('user/login', 'AuthController@authenticate'); $api->post('user/register', 'AuthController@register'); $api->group(['middleware' => 'jwt.auth'], function ($api) { $api->get('lessons', 'LessonController@index'); $api->get('lesson/{id}', 'LessonController@show'); }); });});

9.2 创建 AuthController

定义 user 模型位置
<?php// api\config\jwt.php 'user' => 'App\Models\User',
创建 AuthController,注意这里 use 了 Request
<?php// app\Api\Controllers\AuthController.phpnamespace App\Api\Controllers;use App\Models\User;// 注意这里 use 了 Requestuse Illuminate\Http\Request;use JWTAuth;use Tymon\JWTAuth\Exceptions\JWTException;class AuthController extends BaseController { public function authenticate(Request $request) { // 返回请求中 email 和 password 的值 $credentials = $request->only('email', 'password'); try { // attempt to verify the credentials and create a token for the user if (! $token = JWTAuth::attempt($credentials)) { return response()->json(['error' => 'invalid_credentials'], 401); } } catch (JWTException $e) { // something went wrong whilst attempting to encode the token return response()->json(['error' => 'could_not_create_token'], 500); } // 登录成功后,以 json 形式返回 token 值 return response()->json(compact('token')); } public function register(Request $request) { $newUser = [ 'email' => $request->get('email'), 'name' => $request->get('name'), 'password' => $request->get('password'), ]; $user = User::create($newUser); $token = JWTAuth::fromUser($user); // 注册成功后返回 token return response()->json(compact('token')); } }

9.3 调试

登录调试:post 请求,body 携带 emial 和 password 参数
http://api.dev/api/user/login
注册调试:post 请求,body 携带 emial、name 和 password 参数
http://api.dev/api/user/register
先登录获取 token ,然后显示内容
http://api.dev/api/lesson/1?token=xxxhttp://api.dev/api/lessons?token=xxx

第10章 JWT 其他用法

10.1 根据 token 返回用户信息

路由
<?php// routes\api.php $api->group(['middleware' => 'jwt.refresh'], function ($api) { $api->get('user/info', 'AuthController@getAuthenticatedUser'); });
AuthController
<?php//app\Api\Controllers\AuthController.phpuse Tymon\JWTAuth\Exceptions\JWTException;use Tymon\JWTAuth\Exceptions\TokenExpiredException;use Tymon\JWTAuth\Exceptions\TokenInvalidException; public function getAuthenticatedUser() { try { if (! $user = JWTAuth::parseToken()->authenticate()) { return response()->json(['user_not_found'], 404); } } catch (TokenExpiredException $e) { return response()->json(['token_expired'], $e->getStatusCode()); } catch (TokenInvalidException $e) { return response()->json(['token_invalid'], $e->getStatusCode()); } catch (JWTException $e) { return response()->json(['token_absent'], $e->getStatusCode()); } return response()->json(compact('user')); }

10.2 解决请求字段与数据库不一致问题

注意 password 没有变
<?php//app\Api\Controllers\AuthController.php $credentials = [ 'user_email' => $request->get('user_email'), 'password' => $request->get('user_password'), ];
增加 getAuthPassword 方法
<?php// api\app\Models\User.php public function getAuthPassword() { return $this->user_password; }
资源:
https://coding.net/u/wangyan/p/laravel-api/githttps://www.codecasts.com/series/dive-into-restful-api-with-laravel
If you have any questions, please contact me.