隨着先後端徹底分離,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(