使用thinkphp6搭建后端api接口流程
## 1、下載tp6
首先搭建wamp,或lamp的環境安裝composer,通過composer安裝tp6,thinkphp官網已經不再支持直接下載。
~~~
composer create-project topthink/think tp6
~~~
在下載好的tp6目錄通過cmd命令窗口輸入(如果綁定了域名,直接用域名方面,忽略這段)
~~~
php think run
~~~
在瀏覽器中輸入127.0.0.1:8000,[訪問](https://it.zsyts.cn/tag/%e8%ae%bf%e9%97%ae)到如下頁面就安裝成功了
## 2、打開錯誤調試
1.找到config/app.php下的show\_error\_msg ,改成true
~~~
// 顯示錯誤信息'show_error_msg' => true,
~~~
2.找到下面根目錄下的.example.env文件,重命名此文件,把.example刪掉
查看里面的代碼,會發現,它打開了app\_debug調試
## 3、隱藏入口文件
如果什么都不填,默認訪問的就是index控制器,在config/app.php文件中有這樣的定義,你也可以修改默認的控制器
~~~
// 默認應用'default_app' => 'index',
~~~
還有,不管訪問任何控制器,如果沒有填方法,它都會訪問控制器中的index方法,如果index方法不存在,則提示錯誤信息-方法不存在。
通過在項目根目錄中運行的php think run開啟的web服務,tp6幫我們做了隱藏入口文件的操作,所以你可以通過第三種方式訪問。但是我們這一節要說的就是隱藏入口,怎么能用tp6自帶的web服務呢。所以要自己來。
我們在開發時,往往會在本地搭建WNMP等這樣的一套web解決方案,這就需要我們自己去隱藏入口文件index.php
**為什么要隱藏入口文件?**
1. 因為像這樣子http://127.0.0.1:4321/index.php/index/index訪問方法,這個index.php很不好看。
2. 多余。
3. 危險
實現隱藏index.php很簡單,只需要找到public目錄下的.htaccess文件,添加如下代碼就可以了。
**Apache版本:**
~~~
<IfModule mod_rewrite.c> #如果mode_rewrite.c模塊存在 則執行以下命令 Options +FollowSymlinks -Multiviews RewriteEngine On #開啟 rewriteEngine # !-d 不是目錄或目錄不存在 RewriteCond %{REQUEST_FILENAME} !-d # !-f 不是文件或文件不存在 RewriteCond %{REQUEST_FILENAME} !-f RewriteRule (.*)$ index.php [QSA,PT,L] # 參數解釋 # ^(.*)$: 匹配所有的路口映射 # QSA: (Query String Appending)表示保留參數入get傳值?xxx==xx; # PT: 把這個URL交給Apache處理; # L: 作為最后一條,遇到這條將不再匹配這條之后的規則</IfModule>
~~~
**Nginx版本:**
~~~
location / { if (!-e $request_filename) { rewrite (.*)$ /index.php?s=/$1 last; }}
~~~
## 4、解決跨域問題
在應用開發中,前后端都是分開獨立開發的,而前后端通常都會自己搭建一個web服務,運行在不同的端口上,在前端訪問后端的接口時,會報跨域的錯誤。而這種跨域問題通常是要有后端來處理的,tp6有專門的中間件來做這個事情,真是太方便了,只需要在app目錄下的middleware.php中添加該中間件,就實現了跨域訪問。
~~~
<?php// 全局中間件定義文件return [ // 全局請求緩存 // \think\middleware\CheckRequestCache::class, // 多語言加載 // \think\middleware\LoadLangPack::class, // Session初始化 // \think\middleware\SessionInit::class, // 跨域解決 \think\middleware\AllowCrossDomain::class,];
~~~
## 5、路由解決api版本控制
在app目錄中的container控制器中新建兩個文件夾v1,v2,在其中都創建User.php文件

v1/User.php
~~~
<?phpnamespace app\controller\v1;use app\BaseController;class User extends BaseController { public function login() { return '這是v1接口'; }}
~~~
v2/User.php
~~~
<?phpnamespace app\controller\v2;use app\BaseController;class User extends BaseController { public function login() { return '這是v2接口'; }}
~~~
注意上面兩個文件的命名空間,就第一行代碼,在哪個文件夾下,就寫到哪里。
現在方法有了,我們還無法訪問,需要使用路由,讓路由幫我們找對應的方法。
至于路由的概念去文檔自己看。我[這里](https://it.zsyts.cn/tag/%e8%bf%99%e9%87%8c)主要用路由組的方式,我覺得這個比資源路由好用,靈活。
在根目錄下的route目錄下的app.php文件代碼如下:
~~~
// +----------------------------------------------------------------------// | ThinkPHP [ WE CAN DO IT JUST THINK ]// +----------------------------------------------------------------------// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.// +----------------------------------------------------------------------// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )// +----------------------------------------------------------------------// | Author: liu21st <liu21st@gmail.com>// +----------------------------------------------------------------------use think\facade\Route;// api版本控制$v = request()->header('Api-Version');// 默認api版本為v1if ($v == null) { $v = 'v1';}Route::group('user', function () { Route::post('login', 'login');})->prefix($v.'.user/')->pattern(['id' => 'd+']);
~~~
以上代碼進行控制api版本的方式是,請求發起者在header中傳遞要訪問的api的版本,這里獲取到對應的版本,訪問對應的方法。
鑒于以上我使用的是post請求,且要傳遞header,所以使用ApiPost / postman進行測試。
訪問v1版本的接口時:

訪問v12版本的接口時:

## 6、jwt token驗證
thinkphp常用的擴展插件請參考:http://sites.thinkphp.cn/1556332
~~~
composer require thans/tp-jwt-auth
~~~
安裝完成后,該插件所在的位置在根目錄下的vendor/thans/tp-jwt-auth
還會在根目錄下的config目錄下生成jwt.php文件來記錄一些配置信息

看這里都是讀取的env中的參數,所以咱也在根目錄下的.env文件中配置參數。
在根目錄下打開cmd窗口,執行
~~~
php think jwt:create
~~~
會幫你在.env文件中生成密鑰secret,紅色框中的是新增的內容

token的有效期為60秒,為了方便我們測試,我就不改了,如果你要改,可以在.env中添加,這樣就改成了半小時

這個插件有三種方式【header,token,param】傳遞token,我就使用其中一個,也是最常用的一種,就是在【header】中傳遞token信息,這個插件默認驗證header中的token信息需要傳遞的參數名為authorization,而在header中直接傳遞該參數tp6是獲取不到的,需要做一些設置,
在根目錄中的public目錄下的.htacccess文件中添加(Nginx好像不需要添加也能正常獲取到Authorization)
~~~
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
~~~
~~~
<IfModule mod_rewrite.c> #如果mode_rewrite.c模塊存在 則執行以下命令 Options +FollowSymlinks -Multiviews RewriteEngine On #開啟 rewriteEngine # !-d 不是目錄或目錄不存在 RewriteCond %{REQUEST_FILENAME} !-d # !-f 不是文件或文件不存在 RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php [QSA,PT,L] # 參數解釋 # ^(.*)$: 匹配所有的路口映射 # QSA: (Query String Appending)表示保留參數入get傳值?xxx==xx; # PT: 把這個URL交給Apache處理; # L: 作為最后一條,遇到這條將不再匹配這條之后的規則 # 增加下面這項,否則在header中會獲取不到Authorization SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0</IfModule>
~~~
那么現在開始測試:
**(1).生成token**
我就在之前創建的v1/User.php控制器中寫了
~~~
<?phpnamespace app\controller\v1;use app\BaseController;use thans\jwt\facade\JWTAuth;class User extends BaseController { public function login() { $token = JWTAuth::builder(['uid' => 1, 'name' => '測試1']); return $token; }}
~~~
在ApiPost / postman中測試

**(2).驗證token**
我使用的是路由中間件的方式驗證token,
**① 寫一個中間件**
在根目錄下的app目錄中創建middleware目錄,在其下創建CheckToken.php文件
app/middleware/CheckToken.php
~~~
<?phpnamespace app\middleware;use thans\jwt\exception\JWTException;use thans\jwt\facade\JWTAuth;class CheckToken { public function handle($request, \Closure $next) { // OPTIONS請求直接返回 if ($request->isOptions()){ return response(); } try { JWTAuth::auth(); }catch (JWTException $e){ return json($e->getMessage()); } return $next($request); }}
~~~
**② 起別名**
給該中間件起個別名,在根目錄下的config/middleware.php文件中
~~~
<?php// 中間件配置return [ // 別名或分組 'alias' => [ 'CheckToken' => app\middleware\CheckToken::class ], // 優先級設置,此數組中的中間件會按照數組中的順序優先執行 'priority' => [],];
~~~
**③ 在路由文件中使用中間件**route/app.php
~~~
Route::group('user', function () { Route::post('login', 'login'); Route::post('userInfo', 'getUserInfo')->middleware('CheckToken');})->prefix($v.'.user/')->pattern(['id' => 'd+']);
~~~
**④ 創建對應的方法**
在第三步中我們創建了一個getUserInfo()方法,現在在User.php文件中創建
~~~
public function getUserInfo() { return json(['id'=>1, 'name'=> '啦啦啦']);}
~~~
**⑤ 驗證一下**
剛剛創建的token必然過期了,咱重新獲取一條
現在驗證一下,請求userinfo方法,并在header中添加參數Authorization,
注意:token值需要加上bearer ,bearer后的空格也要的。

過了一分鐘后,我們再來試一試

可以看到token驗證提示,該通過過期了,這個插件成功了,并沒有繼續往下走,把之前的信息返回。
**(3).刷新token,會將舊token加入黑名單**
~~~
JWTAuth::refresh();//刷新token,會將舊token加入黑名單
~~~
**(4).注銷或拉黑token**
~~~
JWTAuth::invalidate($token);
~~~
這個拉黑的具體操作就是把你要注銷的token保存在本地的cookie中,默認的保存時間是14天,14天后cookie會自己刪除的,你可以在根目錄下的runtime目錄下的cache目錄中找到對應的文件,我就不測試這個方法了,我感覺這個操作好像沒什么必要。
**(5).驗證Token是否為黑名單**
~~~
JWTAuth::validate($token);
~~~
## 7、統一的參數返回形式
實際開發中,后端返回給前端的參數往往都是這樣的。

所以我們需要對參數返回形式做個統一的處理
在app目錄下的common.php中定義的方法全局都可調用,所以在這個文件中定義此方法。
~~~
<?phpuse think\Response;// 應用公共文件// 統一返回數據格式function result($data = [], string $msg = 'error', int $code = 200, string $type = 'json') { $result = [ 'code' => $code, 'msg' => $msg, 'data' => $data ]; // 調用Response的create方法,指定code可以改變請求的返回狀態碼 return Response::create($result, $type)->code($code);}
~~~
哪里用的時候直接調用即可
**以下為常用狀態碼:**
200 請求成功
204 請求成功,未返回實體,比如option請求
400 錯誤的請求
401 認證失敗,這個一般在token驗證那里
403 拒絕訪問
404 請求的資源不存在
413 請求實體太大
422 參數驗證錯誤
500 服務器錯誤
## 8、異常捕捉
tp6異常捕獲 http://www.hmoore.net/manual/thinkphp6\_0/1037615
**(1)參數驗證錯誤捕捉**
我們先寫一個參數驗證的類,在app目錄下創建validate目錄,創建User.php文件
app/validate/User.php
~~~
<?phpnamespace app\validate;use think\Validate;class User extends Validate { protected $rule = [ 'name' => 'require|max:25', 'age' => 'number|between:1,120', 'email' => 'email' ]; protected $message = [ 'name.require' => '名稱必須', 'name.max' => '名稱最多不能超過25個字符', 'age.number' => '年齡必須是數字', 'age.between' => '年齡只能在1-120之間', 'email' => '郵箱格式錯誤', ];}
~~~
tp6的異常捕捉分為兩種,自動和手動的,手動的就是通過try{}catch{}捕捉。tp6的異常捕捉大多是自動的,不過,比如我們現在要操作的參數驗證錯誤就需要自己去捕捉來拋出異常,我們此節的目的是統一捕捉這個錯誤,我就不用手動的了。
我們就在異常處理類的render方法中添加這個捕捉拋出就可以了。

~~~
// 1.參數驗證錯誤if($e instanceof ValidateException){ return result($e->getError(), '參數驗證不通過', 422);}
~~~
現在在方法中一下,看看能否捕獲。
app/controller/v1/User.php


如果驗證通過了,就會正常的走下去,則會顯示我return的測試內容

**(2)未匹配到資源或方法的異常捕獲**
~~~
<?phpnamespace app;use ParseError; // 語法錯誤use TypeError;use InvalidArgumentException;use think\db\exception\DataNotFoundException;use think\db\exception\ModelNotFoundException;use think\db\exception\PDOException; // 數據庫連接錯誤use think\db\exception\DbException; // 數據庫模型訪問錯誤,比如方法不存在use think\exception\RouteNotFoundException;use think\exception\ClassNotFoundException;use think\exception\FuncNotFoundException;use think\exception\FileException;use think\exception\Handle;use think\exception\HttpException;use think\exception\HttpResponseException;use think\exception\ValidateException;use think\Response;use Throwable;/** * 應用異常處理類 */class ExceptionHandle extends Handle { /** * 不需要記錄信息(日志)的異常類列表 * @var array */ protected $ignoreReport = [ HttpException::class, HttpResponseException::class, ModelNotFoundException::class, DataNotFoundException::class, ValidateException::class, ]; /** * 記錄異常信息(包括日志或者其它方式記錄) * * @access public * @param Throwable $exception * @return void */ public function report(Throwable $exception): void { // 使用內置的方式記錄異常日志 parent::report($exception); } /** * Render an exception into an HTTP response. * * @access public * @param \think\Request $request * @param Throwable $e * @return Response */ public function render($request, Throwable $e): Response { // 添加自定義異常處理機制 // 請求異常 if ($e instanceof HttpException && $request->isAjax()) { return response($e->getMessage(), $e->getStatusCode()); } // 使用了錯誤的數據類型 或 缺失參數 if ($e instanceof InvalidArgumentException || $e instanceof ErrorException) { $fileUrlArr = explode(DIRECTORY_SEPARATOR, $e->getFile()); $data = [ 'err_msg' => $e->getMessage(), 'file' => $fileUrlArr[count($fileUrlArr) - 1], 'line' => $e->getLine() ]; return result($data, '參數錯誤', 413); } // 1.參數驗證錯誤 if ($e instanceof ValidateException) { return result($e->getError(), '參數驗證不通過', 422); } // 2.方法(控制器、路由、http請求)、資源(多媒體文件,如視頻、文件)未匹配到, // 一旦在定義的路由規則中匹配不到,它就會直接去匹配控制器,但是因為在控制器中做了版本控制v1,v2這樣的,所以它是無法獲取對應控制器的 // 所以都會直接走了HttpException的錯誤 // 感覺好像也無所謂,反正是做api接口的,只不過這樣就不好準確的提示信息了 // 到底這個請求時控制器找不到呢?還是方法找不到?還是請求類型(get,post)不對? if (($e instanceof ClassNotFoundException || $e instanceof RouteNotFoundException) || ($e instanceof HttpException && $e->getStatusCode() == 404)) { $data = [ 'err_msg' => $e->getMessage(), 'tip_1' => '請檢查路徑是否填寫正確', 'tips_2' => '請檢查請求類型是否正確', ]; return result($data, '方法或資源未找到,請檢查', 404); } // 3.語法錯誤 if ($e instanceof ParseError) { $fileUrlArr = explode(DIRECTORY_SEPARATOR, $e->getFile()); $data = [ 'err_msg' => $e->getMessage(), 'file' => $fileUrlArr[count($fileUrlArr) - 1], 'line' => $e->getLine() ]; return result($data, '服務器異常-語法錯誤', 411); } // 4.數據庫錯誤 if ($e instanceof PDOException || $e instanceof DbException) { $fileUrlArr = explode(DIRECTORY_SEPARATOR, $e->getFile()); $data = [ 'err_msg' => $e->getMessage(), 'file' => $fileUrlArr[count($fileUrlArr) - 1], 'line' => $e->getLine() ]; return result($data, '服務器異常-數據庫錯誤', 412); } // 其他錯誤交給系統處理 return parent::render($request, $e); }}
~~~
## 9、自動生成api文檔
**(1)安裝插件**https://hgthecode.github.io/thinkphp-apidoc/guide/install/
~~~
composer require hg/apidoc
~~~
**(2)下載對應的前端頁面**
請根據你安裝的apidoc版本 點擊下載 對應的前端文件
| Apidoc版本 | Github | Gitee(國內推薦) |
| --- | --- | --- |
| v3.1.0 – v3.1.5 | v2.1.3 | v2.1.3 |
| v3.0.0 – v3.0.8 | v2.0.11 | v2.0.11 |
下載完成后[解壓](https://it.zsyts.cn/tag/%e8%a7%a3%e5%8e%8b),將apidoc文件夾拷貝到你的項目 public 目錄下
打開瀏覽器訪問 http://你的域名/apidoc/ ,出現接口文檔頁面,表示安裝成功。
**(3)使用**
具體配置你還得看文檔,我就直接照著最簡單的做了,
我就試一個,將app/controller/v1/User.php寫了注釋,它會讀注釋生成接口文檔
① 引入注釋
app/controller/v1/User.php
~~~
<?phpnamespace app\controller\v1;use app\BaseController;use thans\jwt\facade\JWTAuth;use app\validate\User as UserValidate;// 添加這句,注釋寫法為 @Apidoc參數名(...)use hg\apidoc\annotation as Apidoc;/** * @ApidocTitle("V1") * @ApidocGroup("base") */class User extends BaseController { /** * @ApidocTitle("登錄") * @ApidocUrl("v1.user/login") * @ApidocTag("測試 基礎") * @ApidocParam("username", type="string",require=true, desc="用戶名" ) * @ApidocParam("password", type="string",require=true, desc="密碼" ) * @ApidocReturned("id", type="int", desc="新增用戶的id") */ public function login() { //數據驗證,batch開啟批量驗證 validate(UserValidate::class)->batch(true)->check([ 'name' => 'dongsir', 'email' => 'dongsir@qq.com' ]); return result(null, '成功', 200); }}
~~~
**② 查看效果**

關于這個多應用/多版本的配置項,去apidoc的文檔去看吧,在config/apidoc.php修改apps的配置就可以了,然后就可以通過右上角的選擇框切換版本了
// 設置應用/版本(必須設置)
~~~
'apps' => [ [ 'title'=>'演示示例', 'path'=>'app', 'folder'=>'controller', 'items'=>[ ['title'=>'V1.0','path'=>'appcontroller1','folder'=>'v1'], ['title'=>'V2.0','path'=>'appcontroller2','folder'=>'v2'] ] ], ],
~~~
**聲明:**本站所有文章,如無特殊說明或標注,均為互聯網收集。如若本站內容侵犯了原著者的合法權益,可聯系我們進行處理。
- 空白目錄
- 使用thinkphp6搭建后端api接口流程
- tp6 uniapp vue 前后端跨域解決方案
- 操作記錄
- api00
- 你看看有沒有用
- 6666
- Docker安裝LNMP環境的詳細過程(可部署TP項目)
- LNMP部署thinkphp
- 玩客云Armbian 安裝LNMP環境 Docker
- ThinkPHP6項目基操(16.實戰部分 redis+token登錄)
- ThinkPHP6項目基操(11.實戰部分 部署后臺靜態頁面模板及后臺登錄頁面)
- ThinkPHP6項目基操(13.實戰部分 項目中的自定義異常處理總結 錯誤頁面API錯誤)
- ThinkPHP6項目基操(14.實戰部分 中間件處理登錄流程)
- ThinkPHP6項目基操(12.實戰部分 驗證碼)
- ThinkPHP6項目基操(18.實戰部分 表單令牌Token 防CSRF)
- ThinkPHP6項目基操(19.實戰部分 Mysql模型事務操作)
- ThinkPHP6項目基操(20.實戰部分 數據庫操作返回值總結)
- 瀏覽器端判斷當前設備的運行環境
- api
- api異常捕捉
- 寫一個中間件
- 統一的參數返回形式
- ThinkPHP6調用模型的方法
- thinkphp6控制器、驗證器、模型、service,各層寫的內容