随着先后端彻底分离,PHP
也基本告别了 view
模板嵌套开发,转而专门写资源接口。Laravel
是 PHP 框架中最优雅的框架,国内也愈来愈多人告别 ThinkPHP
选择了 Laravel
。Laravel
框架自己对 API
有支持,可是感受再工做中仍是须要再作一些处理。Lumen
用起来不顺手,有些包不能很好地支持。因此,将 Laravel
框架进行一些配置处理,让其在开发 API
时更驾轻就熟。php
固然,你也能够点击这里 , 直接跳到成果~前端
PHP > 7.1 MySQL > 5.5 Redis > 2.8
postman composer
为了模拟 AJAX 请求,请将 header头
设置 X-Requested-With
为 XMLHttpRequest
laravel
Laravel
只要 >=5.5
皆可,这里采用文章编写时最新的 5.7
版本git
composer create-project laravel/laravel Laravel --prefer-dist "5.7.*"
CREATE TABLE `users` ( `id` INT UNSIGNED NOT NULL PRIMARY KEY auto_increment COMMENT '主键ID', `name` VARCHAR ( 12 ) NOT NULL COMMENT '用户名称', `password` VARCHAR ( 80 ) NOT NULL COMMENT '密码', `last_token` text COMMENT '登录时的token', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '用户状态 -1表明已删除 0表明正常 1表明冻结', `created_at` TIMESTAMP NULL DEFAULT NULL COMMENT '建立时间', `updated_at` TIMESTAMP NULL DEFAULT NULL COMMENT '修改时间' ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci;
在项目的 app
目录下能够看到,有一个 User.php
的模型文件。由于 Laravel
默认把模型文件放在 app
目录下,若是数据表多的话,这里模型文件就会不少,不便于管理,因此咱们先要将模型文件移动到其余文件夹内。github
1) 在 app
目录下新建 Models
文件夹,而后将 User.php
文件移动进来。
2) 修改 User.php
的内容web
<?php namespace App\Models; //这里从App改为了App\Models use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable; protected $table = 'users'; //去掉我建立的数据表没有的字段 protected $fillable = [ 'name', 'password' ]; //去掉我建立的数据表没有的字段 protected $hidden = [ 'password' ]; //将密码进行加密 public function setPasswordAttribute($value) { $this->attributes['password'] = bcrypt($value); } }
3) 由于有关于 User 的命名空间发生了改变,因此咱们全局搜索 App\User
, 将其替换为 App\Models\User
. 我一共搜索到 4 个文件ajax
app/Http/Controllers/Auth 目录下的 RegisterController.php config 目录下的 services.php config 目录下的 auth.php database/factories 目录下的 UserFactory.php
由于是专门作 API 的,因此咱们要把是 API 的控制器都放到 app\Http\Controllers\Api
目录下。redis
使用命令行建立控制器算法
php artisan make:controller Api/UserController
编写 app/Http/Controllers/Api
目录下的 UserController.php
文件数据库
<?php namespace App\Http\Controllers\Api; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class UserController extends Controller { // public function index(){ return 'guaosi'; } }
这里写了 index 函数,用来下面创建路由后的测试,查看是否能够正常访问。
在 routes
目录下的 api.php
是专门用来写 Api 接口的路由,因此咱们打开它,填写如下内容,作一个测试.
<?php use Illuminate\Http\Request; Route::namespace('Api')->prefix('v1')->group(function () { Route::get('/users','UserController@index')->name('users.index'); });
由于咱们 Api 控制器的命名空间是
App\Http\Controllers\Api
, 而Laravel
默认只会在命名空间App\Http\Controllers
下查找控制器,因此须要咱们给出namespace
。同时,添加一个
prefix
是为了版本号,方便后期接口升级区分。
打开 postman
, 用 get
方式请求你的域名/api/v1/users
, 最后返回结果是
guaosi
则成功
在建立用户以前,咱们先建立验证器,来让咱们服务器接收到的数据更安全。固然,咱们也要把关于 Api 验证的放在一个专门的文件夹内。
先建立一个 Request
的基类
php artisan make:request Api/FormRequest
由于验证器默认的权限验证是 false
,致使返回都是 403
的权限不经过错误。这里咱们没有用到权限认证,为了方便处理,咱们默认将权限都是经过的状态。因此,每一个文件都须要咱们将 false
改为 true
。
public function authorize() { //false表明权限验证不经过,返回403错误 //true表明权限认证经过 return true; }
因此咱们修改 app/Http/Requests/Api
目录下的 FormRequest.php
文件
<?php namespace App\Http\Requests\Api; use Illuminate\Foundation\Http\FormRequest as BaseFormRequest; class FormRequest extends BaseFormRequest { public function authorize() { //false表明权限验证不经过,返回403错误 //true表明权限认证经过 return true; } }
这样这个命名空间下的验证器都会默认经过权限验证。固然,若是你须要权限验证,能够经过直接覆盖方法。
接着咱们开始建立关于 UserController
的专属验证器
php artisan make:request Api/UserRequest
编辑 app/Http/Requests/Api
目录下的 UserRequest.php
文件
<?php namespace App\Http\Requests\Api; class UserRequest extends FormRequest { public function rules() { switch ($this->method()) { case 'GET': { return [ 'id' => ['required,exists:shop_user,id'] ]; } case 'POST': { return [ 'name' => ['required', 'max:12', 'unique:users,name'], 'password' => ['required', 'max:16', 'min:6'] ]; } case 'PUT': case 'PATCH': case 'DELETE': default: { return [ ]; } } } public function messages() { return [ 'id.required'=>'用户ID必须填写', 'id.exists'=>'用户不存在', 'name.unique' => '用户名已经存在', 'name.required' => '用户名不能为空', 'name.max' => '用户名最大长度为12个字符', 'password.required' => '密码不能为空', 'password.max' => '密码长度不能超过16个字符', 'password.min' => '密码长度不能小于6个字符' ]; } }
如今咱们来编写建立用户接口,制做一些虚拟数据。(就不使用 seeder 来填充了)
打开 UserController.php
//用户注册 public function store(UserRequest $request){ User::create($request->all()); return '用户注册成功。。。'; } //用户登陆 public function login(Request $request){ $res=Auth::guard('web')->attempt(['name'=>$request->name,'password'=>$request->password]); if($res){ return '用户登陆成功...'; } return '用户登陆失败'; }
而后咱们建立路由,编辑 api.php
Route::post('/users','UserController@store')->name('users.store'); Route::post('/login','UserController@login')->name('users.login');
打开 postman
, 用 post
方式请求你的域名/api/v1/users
, 在 form-data
记得填写要建立的用户名和密码。
最后返回结果是
用户建立成功。。。
则成功。
若是返回
{ "message": "The given data was invalid.", "errors": { "name": [ "用户名不能为空" ], "password": [ "密码不能为空" ] } }
则证实验证失败。
而后验证是否能够正常登陆。由于咱们认证的字段是 name
跟 password
, 而 Laravel
默认认证的是 email
跟 password
。因此咱们还要打开 app/Http/Controllers/auth
目录下的 LoginController.php
, 加入以下代码
public function username() { return 'name'; }
打开 postman
, 用 post
方式请求你的域名/api/v1/login
最后返回结果是
用户登陆成功...
则成功
为了测试使用,请自行经过接口建立 10 个用户。
给出总体控制器信息 UserController.php
<?php namespace App\Http\Controllers\Api; use App\Http\Requests\Api\UserRequest; use App\Models\User; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class UserController extends Controller { //返回用户列表 public function index(){ //3个用户为一页 $users = User::paginate(3); return $users; } //返回单一用户信息 public function show(User $user){ return $user; } //用户注册 public function store(UserRequest $request){ User::create($request->all()); return '用户注册成功。。。'; } //用户登陆 public function login(Request $request){ $res=Auth::guard('web')->attempt(['name'=>$request->name,'password'=>$request->password]); if($res){ return '用户登陆成功...'; } return '用户登陆失败'; } }
给出总体路由信息 api.php
<?php use Illuminate\Http\Request; Route::namespace('Api')->prefix('v1')->group(function () { Route::get('/users','UserController@index')->name('users.index'); Route::get('/users/{user}','UserController@show')->name('users.show'); Route::post('/users','UserController@store')->name('users.store'); Route::post('/login','UserController@login')->name('users.login'); });
以上全部返回的结果,不管正确或者错误,都没有一个统一格式规范,对开发 Api
不太友好的,须要咱们进行一些修改,让 Laravel 框架能够更加友好地编写 Api。
全部问题,跨域先行。跨域问题没有解决,一切处理都是纸老虎。这里咱们使用 medz 作的 cors 扩展包
composer require medz/cors
php artisan vendor:publish --provider="Medz\Cors\Laravel\Providers\LaravelServiceProvider" --force
打开 config/cors.php
, 在 expose-headers
添加值 Authorization
return [ ...... 'expose-headers' => ['Authorization'], ...... ];
这样跨域请求时,才能返回
header
头为Authorization
的内容,不然在刷新用户token
时不会返回刷新后的token
打开 app/Http/Kernel.php
, 增长一行
protected $routeMiddleware = [ ...... //前面的中间件 'cors'=> \Medz\Cors\Laravel\Middleware\ShouldGroup::class, ];
打开 routes/api.php
, 在路由组中增长使用中间件
Route::namespace('Api')->prefix('v1')->middleware('cors')->group(function () { Route::get('/users','UserController@index')->name('users.index'); Route::get('/users/{user}','UserController@show')->name('users.show'); Route::post('/users','UserController@store')->name('users.store'); Route::post('/login','UserController@login')->name('users.login'); });
接口主流返回 json
格式,其中包含 http状态码
,status请求状态
,data请求资源结果
等等。须要咱们有一个 API 接口全局都能有统一的格式和对应的数据处理。参考于这里。
在 app/Api/Helpers
目录 (不存在目录本身新建) 下新建 ApiResponse.php
填入以下内容
<?php namespace App\Api\Helpers; use Symfony\Component\HttpFoundation\Response as FoundationResponse; use Response; trait ApiResponse { /** * @var int */ protected $statusCode = FoundationResponse::HTTP_OK; /** * @return mixed */ public function getStatusCode() { return $this->statusCode; } /** * @param $statusCode * @return $this */ public function setStatusCode($statusCode,$httpCode=null) { $httpCode = $httpCode ?? $statusCode; $this->statusCode = $statusCode; return $this; } /** * @param $data * @param array $header * @return mixed */ public function respond($data, $header = []) { return Response::json($data,$this->getStatusCode(),$header); } /** * @param $status * @param array $data * @param null $code * @return mixed */ public function status($status, array $data, $code = null){ if ($code){ $this->setStatusCode($code); } $status = [ 'status' => $status, 'code' => $this->statusCode ]; $data = array_merge($status,$data); return $this->respond($data); } /** * @param $message * @param int $code * @param string $status * @return mixed */ /* * 格式 * data: * code:422 * message:xxx * status:'error' */ public function failed($message, $code = FoundationResponse::HTTP_BAD_REQUEST,$status = 'error'){ return $this->setStatusCode($code)->message($message,$status); } /** * @param $message * @param string $status * @return mixed */ public function message($message, $status = "success"){ return $this->status($status,[ 'message' => $message ]); } /** * @param string $message * @return mixed */ public function internalError($message = "Internal Error!"){ return $this->failed(