ThinkPHP Restful API 開發流程及小技巧
最近學習了七月老師的ThinkPHP+小程式的實戰課程,對用於ThinkPHP開發Rest api介面有了更深的體會,課程中七月老師的清晰的模組化程式設計思想也給我有了很大的感觸。希望通過此篇文章整理課程開發思路的同時,也能給對這方面有學習興趣的同學提供些許幫助。
一、要學習什麼是 RESTful API 首先我們得明白什麼是REST?
簡單來說:REST是所有Web應用都應該遵守的架構設計指導原則。 英文全詞Representational State Transfer,翻譯是”表現層狀態轉化”。
面向資源 是REST最明顯的特徵,對於同一個資源的一組不同的操作。資源是伺服器上一個可命名的抽象概念,資源是以名詞為核心來組織的,首先關注的是名詞。REST要求,必須通過統一的介面來對資源執行各種操作。對於每個資源只能執行一組有限的操作。(7個HTTP方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS)。
簡單來說就是將伺服器的資料看做是資源,而RESTFUL API 就是給外部使用改變資源的一種操作方法,其中符合REST架構設定是RESTFUL API 最大的特點。其中比較典型的就是HTTP協議。
關於rest 的思想,有興趣的同學可以參考閱讀以下文章
理解本身的REST架構風格
http://www.infoq.com/cn/articles/understanding-restful-style/
理解RESTful架構
http://www.ruanyifeng.com/blog/2011/09/restful.html
Restful API設計指南
http://www.ruanyifeng.com/blog/2014/05/restful_api.html
二、ThinPHP5(以下簡稱TP5)RESTFUL API 開發流程及注意點
1. 設定API Route
在TP5 中通過route.php 即可進行路由的操作,出於安全和程式碼迭代考慮,應注意以下點:
- 新增API 版本目錄
- 指定路由訪問方式 get/post
- 介面引數過濾
- API 路由介面分組
示例程式碼 :
//Route::rule('路由表示式', '路由地址', '請求型別', '路由引數(陣列)', '變數規則(陣列)');
Route::get('api/:version/Theme/:id', 'api/:version.Theme/getComplexOne' ,[],['id'=>'\d+'] );
2.編寫控制器
示例程式碼:
/*
* 獲取product
* @url /theme/:id
*/
public function getComplexOne($id){
//呼叫自定義驗證
(new IDMustBePostiveInt())->goCheck();//著重點
//呼叫模型操作方法
$theme = ThemeModel::getThemeWithProducts($id);
if (!$theme){
//丟擲自定義異常
throw new ThemeException();//著重點
}
return $theme;
}
通過MVC的架構和基於TP5提供 Validate 和 Exception 編寫的 自定義驗證 和 錯誤反饋,只需要通過寥寥幾行程式碼,就可以完成控制器邏輯的編寫,這無論對工作量來說,還是以後調優以及程式碼的可閱讀性,都是大大的提高,下面著重說一下,自定義驗證 和 錯誤反饋
3.編寫自定義驗證類 Validate
目錄結構:
示例程式碼:
IDMustBePostiveInt.php
編寫自定義驗證器,如這個驗證器為ID必須是正整數,在任何需要ID驗證時就都可以使用。
namespace app\api\validate;
class IDMustBePostiveInt extends BaseValidate
{
protected $rule = [
'id' => 'require|isPositiveInteger',
];
protected $message = [
'id' => 'id必須是正整數'
];
}
BaseValidate.php
編寫自定義驗證器,可以擴充套件TP5驗證庫,主要存放公用的驗證方法,方便呼叫。
namespace app\api\validate;
use think\Validate;
class BaseValidate extends Validate
{
//進行引數校驗
public function goCheck()
{
// 獲取http引數
//引數校驗
$request = Request::instance();
$params = $request->param();
$result = $this->batch()->check($params);
if (!$result) {
$e = new ParameterException([
'msg' => $this->error,
/*'code'=> 400,
'errorCode' => 1002*/
]);
throw $e;
} else {
return true;
}
}
//判斷是否是正整數
protected function isPositiveInteger($value, $rule = '', $data = '', $field = '')
{
if (is_numeric($value) && is_int($value + 0) && ($value + 0) > 0) {
return true;
} else {
return false;
}
}
}
在日常工作中,通過會使用到引數驗證,這一點在API開發中尤為重要,可以大大提高介面的安全性,但是往往TP5自帶的Validate 驗證方法是不夠的,通過編寫驗證器擴充套件,可以圓滿的補充這一點,而且也大大提高了程式碼的複用性,減少了工作量。
4.編寫自定義錯誤類 Exception
檔案結構:
示例程式碼:
ThemeException .php
自定義錯誤類,可指定返回的錯誤資訊
namespace app\lib\exception;
class ThemeException extends BaseException
{
// HTTP 狀態碼 400,200
public $code = 400;
// 錯誤資訊
public $msg = '請求的主題不存在';
//自定義錯誤碼
public $errorCode = 30000;
}
BaseException .php
存放預設反饋資訊,通過動態修改其中的預設資訊,達到動態反饋的效果。
namespace app\lib\exception;
use think\Exception;
class BaseException extends Exception
{
// HTTP 狀態碼 400,200
public $code = 400;
// 錯誤資訊
public $msg = '引數錯誤';
//自定義錯誤碼
public $errorCode = 10000;
public function __construct($params = [])
{
if (!is_array($params))
{
return;
// throw new Exception('引數必須是陣列');
}
if (array_key_exists('code', $params))
{
$this->code = $params['code'];
}
if (array_key_exists('msg', $params))
{
$this->msg = $params['msg'];
}
if (array_key_exists('errorCode', $params))
{
$this->errorCode = $params['errorCode'];
}
}
}
在API開發中,介面的錯誤反饋尤為重要,可以為產品提供更準確的資訊,通常與之相對應的還有一張API反饋資訊表,通過查詢該表,前端人員可以快速有效的找準錯誤。而對伺服器本身的錯誤而言,通常是不需要反饋給客戶端的,通過編寫自定義反饋類,也可以做到這一點。
5.編寫模型及關聯
示例程式碼:
Theme.php
作用:編寫關聯模型和資料庫邏輯操作,指定需要查詢的欄位,等等。
namespace app\api\model;
class Theme extends BaseModel
{
//關聯模型
public function topicImg(){
return $this->belongsTo('Image','topic_img_id','id');
}
public function headImg()
{
return $this->belongsTo('Image','head_img_id','id');
}
public function products(){
return $this->belongsToMany('product','theme_product','product_id','theme_id');
}
public static function getThemeWithProducts($id){
//使用關聯模型查詢
$theme = self::with('products,topicImg,headImg')->find($id);
return $theme;
}
}
BaseModel .php
作用:存放公用的模型修改方法,在這裡為imgUrl的字首修改。
namespace app\api\model;
use think\Model;
class BaseModel extends Model
{
//讀取更改img模型的url值--讀取器
protected function prefixImgUrl($value,$data){
$finalUrl = $value;
if ($data['from'] == 1)
{
$finalUrl = config('setting.img_prefix').$value;
}
return $finalUrl;
}
}
Image.php
指定對ImageUrl字首。
namespace app\api\model;
class Image extends BaseModel
{
//隱藏不需要返回的欄位
// protected $hidden = ['delete_time','id','update_time','from'];
protected $visible = ['url'];
//讀取更改img模型的url值--讀取器
public function getUrlAttr($value,$data){
return $this->prefixImgUrl($value,$data);
}
}
至此,一個結構優美,程式碼複用性高的API開發大框就出現規模了,篇幅有限,有許多小的知識點如SerService層,Token 編寫,快取等就不在一一贅述。