二:創建http api的基本套路、商品API模擬、RequestMapping注解


(1):http api的基本套路

經常要翻閱的文檔

現在我們舉個例子:

Product商品項目,擬定的API如下

GET /product        ------顯示商品列表

GET /product/123 -------顯示商品ID為123的商品詳情

 

Controller 控制器

 

控制器作為HTTP服務的核心組件,串接起一次請求的整個生命周期. 通過 注解 的方式,相較於傳統的 Controller,代碼更簡潔,用戶可以更關注業務邏輯。

 

創建控制器

 

主要通過 @Controller 注解實現。代碼可以放置任意位置,不過為了統一標准,建議放在 app/Http/Controller

 

可以通過 swoftcli 快速創建新的控制器

 

php swoftcli.phar gen:http-ctrl Product --prefix /Product
(我這里已經將swoftcli.phar 修改成別名 swoftcli於是直接執行swoftcli gen:http-ctrl Product --prefix /Product即可)
執行此命令可以在app\Http\Controller下創建一個名為 ProductController.php的控制器文件,路由前綴為/Product
當然我們也可以手動進行創建控制器;
例如我們手動創建個Http的 Product.php控制器文件

@Controller 注解

Http 控制器類注解 @Controller

  • 注解類:Swoft\Http\Server\Annotation\Mapping\Controller
  • 作用范圍:CLASS
  • 擁有屬性:
    • prefix 指定路由前綴

通常僅有 @Controller 是沒有什么效果的,它需要配合接下來的 @RequestMapping 一起才能正確的工作。

 
        

路由規則

 
        
  • 顯式指定路由前綴:@Controller(prefix="/index")@Controller("/index")
  • 隱式指定路由前綴:@Controller() 默認自動使用 小駝峰 格式解析 controller class 的名稱。
  • 一個完整的路由規則是通過 @Controller + @RequestMapping 注解實現,通常前者定義前綴,后者定義后綴。關於 @RequestMapping 注解將在稍后 路由-@RequestMapping 章節將會詳細介紹;
 

示例: 根據下方的定義,對應的訪問路由是 /v1/users/list (/v1/users + list)

 
        
/** * @Controller(prefix="/v1/users") */ class UsersController { /** * @RequestMapping(route="list") */ public function list(){} } 
 
        

示例:@Controller() 參數為空,則會使用隱式路由前綴綁定,例如下方的定義,對應的訪問路由是 /user/list

 
        
/** * @Controller() */ class UsersController { /** * @RequestMapping(route="list") */ public function list(){} }
***警告***在 Swoft 里不要按照傳統的 fpm 框架繼承父類控制器的成員屬性在其他控制器使用,這種做法是錯誤的。

錯誤示范:

/** * @Controller() */ class BaseController { protected $num; } /** * @Controller(prefix="/v1/index") */ class IndexController extends BaseController { /** * @RequestMapping(route="index") */ public function index() { $this->num++; echo $this->num."\n"; } } 

(2):路由
 
        

  Swoft 與傳統的 PHP 框架不一樣,並沒有采用配置文件的方式來配置路由,而采用了注解。在 Swoft 里我們可以使用 @RequestMapping 注解快速的添加路由。

  

  路由配置

  這是默認的路由配置

// at file: vendor/swoft/http-server/src/AutoLoader.php 'httpRouter' => [ 'name' => 'swoft-http-router', // config 'ignoreLastSlash' => true, 'tmpCacheNumber' => 500, // 'handleMethodNotAllowed' => false ],

配置說明

  • ignoreLastSlash bool 默認:true 是否忽略 URI path 最后的 /

    • 如果設置為 false 不忽略, /home/home/ 將是兩個不同的路由
  • tmpCacheNumber int 默認:500 動態路由緩存數量。

    • 動態參數路由匹配后會緩存下來,下次相同的路由將會更快的匹配命中。
  • handleMethodNotAllowed bool 默認:false 是否處理 MethodNotAllowed

    • 為了加快匹配速度,默認 method 不匹配也是直接拋出 Route not found 錯誤。如有特殊需要可以開啟此選項,開啟后將會拋出 Method Not Allowed 錯誤

  若你需要自定義路由配置,直接在 app/bean.php 添加 httpRouter 項配置即可。

 'httpRouter' => [ 'handleMethodNotAllowed' => true ]

@RequestMapping 注解

Http 控制器類中方法路由注解 @RequestMapping

 
        
  • route 路由規則path
  • method 請求方式(GET、POST、PUT、PATCH、DELETE、OPTIONS、HEAD)
  • params 可以通過它為path變量添加正則匹配限制
 
        
每個方法上盡量只寫一個 @RequestMapping 注解,以免出現紊亂。

路由規則

  • 通常情況,一個完整的路由 path 等於 @Controllerprefix + @RequestMappingroute特殊的,當你的 @RequestMapping 上的路由以 / 開頭時,那完整的路由就是它,即不會再將 prefix 添加到它的前面
    • 顯示指定路由后綴:@RequestMapping("index")@RequestMapping(route="index")
    • 隱式指定路由后綴: 使用 @RequestMapping() 默認解析方法名為后綴
  • 特殊的,當你的 @RequestMapping 上的路由以 / 開頭時,那完整的路由就是它,即不會再將 prefix 添加到它的前面

示例: 在控制器方法中加入 @RequestMapping 注解

 

 

/** * @Controller() */ class UserController { /** * @RequestMapping() */ public function index() {} /** * @RequestMapping("index") */ public function index() {} /** * @RequestMapping(route="index") */ public function index() {} }
代碼執行后將會為 index 方法綁定路由為 /user/index,允許的請求方法為默認的 GETPOST;

綁定路由 path 參數

 
          
  • 指定路由參數: @RequestMapping(route="index/{name}"),Action 方法中可以直接使用 $name 作為方法參數
  • 當路由參數被 [] 包起來則 URL path 傳遞參數是可選的。注意,可選符只能用在最后面
    • 示例1: @RequestMapping("/index[/{name}]") 這樣 /index` /index/tom` 都可以訪問到
    • 示例2: @RequestMapping("/about[.html]") 相當於偽靜態,/about` /about.html` 都可以訪問到
 
          

設置路由請求方式

 
          

如果想要設置允許請求控制器的 HTTP 請求方式。 可以使用方法在控制器中的 @RequestMapping 注解配置 method 參數,可以是 GETPOSTPUTPATCHDELETEOPTIONSHEAD 中的一個或多個。

 
          
  • 限定 HTTP 方法:@RequestMapping(method={RequestMethod::GET}) 指定路由支持的 HTTP 方法,默認是支持 GETPOST
 
          

請切記要引入相關的注解類

 
          
  • Swoft\Http\Server\Annotation\Mapping\RequestMapping
  • Swoft\Http\Server\Annotation\Mapping\RequestMethod
 
          

獲取路由匹配結果

 
          

你可以在中間件或者 action 拿到路由匹配的結果信息。

 

[$status, $path, $route] = $request->getAttribute(Request::ROUTER_ATTRIBUTE);

 

<?php
namespace App\Http\Controller;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
/**
* 商品模塊
* @Controller(prefix="/product")
*/
class Product
{
/**
* @RequestMapping(route="product",method={RequestMethod::GET})
*/
//use Swoft\Http\Message\Request; 引入Request類;
//方法注入

public function prod_list(Request $request)
{
return $request->getAttribute(Request::ROUTER_ATTRIBUTE); //返回結果值 [1,"\/product\/product",{}]
}
}

Http 請求對象

 
          

Swoft 的請求與響應實現於 PSR-7 規范。請求與響應對象存在於每次 HTTP 請求。

 
          
  • 請求對象 RequestSwoft\Http\Message\Request
  • 響應對象 ResponseSwoft\Http\Message\Response
 
          

PSR-7 接口為請求和響應對象提供了這些公共方法:

 
          
  • withProtocolVersion($version)
  • withHeader($name, $value)
  • withAddedHeader($name, $value)
  • withoutHeader($name)
  • withBody(StreamInterface $body)
 
          
 
          

PSR-7 接口為請求對象提供了這些方法:

 
          
  • withMethod(string $method)
  • withUri(UriInterface $uri, $preserveHost = false)
  • withCookieParams(array $cookies)
  • withQueryParams(array $query)
  • withUploadedFiles(array $uploadedFiles)
  • withParsedBody($data)
  • withAttribute($name, $value)
  • withoutAttribute($name)
更多請參考 PSR-7 和 查看 swoft/http-message 中具體的實現類

***警告真相****:根據PSR-7對象的不可變性(immutable),所有的 with* 方法都是克隆對象然后返回,必須接收新對象來做進一步處理,或使用鏈式調用

獲取請求對象

  • 通過控制器方法參數注入 public function action(Request $request)
  • 通過請求上下文獲取 Swoft\Context\Context::mustGet()->getRequest()//已經過期 推薦使用 context()->getRequest();
  

示例: 獲取請求動作

 
          
$request = context()->getRequest(); $method = $request->getMethod();

代碼演示:
<?php
namespace App\Http\Controller;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
/**
* 商品模塊
* @Controller(prefix="/product")
*/
class Product
{
/**
* @RequestMapping(route="prod_context",method={RequestMethod::GET,RequestMethod::POST})
* @throws \Swoft\Exception\SwoftException
*/
public function prod_context()
{
$request = context()->getRequest();
$method = $request->getMethod();
return $method;// 返回結果:GET
}
}

示例: 獲取請求的 URI

每個 HTTP 請求都有一個 URI 標識所請求的應用程序資源。HTTP 請求 URI 有幾個部分:

  • Scheme (e.g. http or https)
  • Host (e.g. example.com)
  • Port (e.g. 80 or 443)
  • Path (e.g. /users/1)
  • Query string (e.g. sort=created&dir=asc)

你可以通過請求對象的 getUri() 方法獲取 PSR-7 URI對象:

$method = $request->getUri();

PSR-7 請求對象的 URI 本身就是一個對象,它提供了下列方法檢查 HTTP 請求的 URL 部分

  • getScheme()
  • getAuthority()
  • getUserInfo()
  • getHost()
  • getPort()
  • getPath()
  • getQuery() (e.g. a=1&b=2)
  • getFragment()
 
          
<?php
namespace App\Http\Controller;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
/**
* 商品模塊
* @Controller(prefix="/product")
*/
class Product
{
/**
* @RequestMapping(route="get_uri")
* @throws \Swoft\Exception\SwoftException
*/
public function get_uri()
{
$uriInfo=array();
$request = context()->getRequest();
$getUri = $request->getUri();
$getScheme=$request->getUri()->getScheme();
$getAuthority=$request->getUri()->getAuthority();
$getUserInfo=$request->getUri()->getUserInfo();
$getHost=$request->getUri()->getHost();
$getPort=$request->getUri()->getPort();
$getPath = $request->getUri()->getPath();
$getQuery=$request->getUri()->getQuery();// (e.g. a=1&b=2);
$getFragment=$request->getUri()->getFragment();

//請求路徑 http://192.168.1.120:9500/product/get_uri?a=1&b=2
$uriInfo['getUri']=$getUri;//getUri {}
$uriInfo['getScheme']=$getScheme;//getScheme http
$uriInfo['getAuthority']=$getAuthority;//getAuthority 192.168.1.120:9500
$uriInfo['getUserInfo']=$getUserInfo;//getUserInfo
$uriInfo['getHost']=$getHost;//getHost 192.168.1.120
$uriInfo['getPort']=$getPort;//getPort 9500
$uriInfo['getPath']=$getPath;//getPath /product/get_uri
$uriInfo['getQuery']=$getQuery;//getQuery a=1&b=2
$uriInfo['getFragment']=$getFragment;//getFragment
return json_encode($uriInfo);
}

}

示例: 獲取請求 Headers

全部的 Headers

$headers = $request->getHeaders(); foreach ($headers as $name => $values) { echo $name . ": " . implode(", ", $values).PHP_EOL; } 

指定的 Header

$headerValueArray = $request->getHeader('host'); print_r($headerValueArray); // return Array $host = $request->getHeaderLine("host"); print_r($host); // return String 
<?php
namespace App\Http\Controller;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
/**
* 商品模塊
* @Controller(prefix="/product")
*/
class Product
{
/**
* @RequestMapping(route="get_header",method={RequestMethod::GET})
* @throws \Swoft\Exception\SwoftException
*/
public function get_header()
{
$request = context()->getRequest();
$headers = $request->getHeaders();
$headerValueArray = $request->getHeader('host');
$host = $request->getHeaderLine("host");
print_r($headerValueArray); // return Array
print_r($host); // return String
/*Array
(
[0] => 192.168.1.120:9500
)
192.168.1.120:9500*/
}
 
          

GET 數據

 
          
$data = $request->query(); $some = $request->query('key', 'default value') $data = $request->get(); $some = $request->get('key','default value');

<?php
namespace App\Http\Controller;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
/**
* 商品模塊
* @Controller(prefix="/product")
*/
class Product
{
/**
* @RequestMapping(route="get_query")
* @throws \Swoft\Exception\SwoftException
*/
public function get_query()
{
//請求路徑:http://192.168.1.122:9500/product/get_query?a=7
$request=context()->getRequest();
/* $data = $request->query();
$some = $request->query('a',"ttt");
return $some;//返回值 7*/
$data = $request->get();
$some = $request->get('a','999');
return $some;//返回值 7
}
 
          
 
          

POST 數據

 
          
$data = $request->post(); $some = $request->post('key', 'default value')

<?php
namespace App\Http\Controller;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
/**
* 商品模塊
* @Controller(prefix="/product")
*/
class Product
{
/**
* @RequestMapping(route="post_query")
* @throws \Swoft\Exception\SwoftException
*/
public function post_query()
{
$request=context()->getRequest();
$data = $request->post();
//return $data; 返回值數組
/* {
"name": "痞子胥",
"age": "30",
"sex": ""
}*/
$some = $request->post('name', 'default value');
return $some; //返回值數組
/* {
"data": "痞子胥"
}*/
}
 
          
 
          

無需關心請求的數據格式,json` xml請求都會自動解析為php的數組數據。都可以通過$request->post()` 獲取。

 
          

GET & POST 數據

 
          
$data = $request->input(); $some = $request->input('key', 'default value') 
 
          
<?php
namespace App\Http\Controller;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
/**
* 商品模塊
* @Controller(prefix="/product")
*/
class Product
{
/**
* @RequestMapping(route="get_post_query")
* @throws \Swoft\Exception\SwoftException
*/
public function get_post_query()
{
//post或者get提交數據的通用寫法
$request=context()->getRequest();
$data = $request->input();
return $data;
//$some = $request->input('key', 'default value');
//return $some;
}
}

RAW 數據

 
          
$data = $request->raw(); 
 
          

SERVER 數據

 
          
$data = $request->getServerParams(); $some = $request->server('key', 'default value');
<?php
namespace App\Http\Controller;
use Swoft\Http\Message\Request;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
/**
* 商品模塊
* @Controller(prefix="/product")
*/
class Product
{
/**
* @RequestMapping(route="get_raw")
* @throws \Swoft\Exception\SwoftException
*/
public function get_server()
{
$request=context()->getRequest();
$data = $request->getServerParams();
return $data;
//返回數據
/* {
"request_method": "POST",
"request_uri": "/product/get_raw",
"path_info": "/product/get_raw",
"request_time": 1578108536,
"request_time_float": 1578108536.066696,
"server_protocol": "HTTP/1.1",
"server_port": 18306,
"remote_port": 55453,
"remote_addr": "192.168.1.108",
"master_time": 1578108535
}*/
}

獲取上傳文件

 
          
$file = $request->getUploadedFiles(); 
 
          

獲取的結果是一維數組或者二位數組,數據結構如下。 若表單中上傳的是單文件則返回的是一個一維數組,數組內容是 Swoft\Http\Message\Upload\UploadedFile 文件對象,例如文件字段名為 file 則數據結構如下

 
          
array(1) { ["file"]=> object(Swoft\Http\Message\Upload\UploadedFile)#6510 (7) { ["size":"Swoft\Http\Message\Upload\UploadedFile":private]=> int(1319) ["errorCode":"Swoft\Http\Message\Upload\UploadedFile":private]=> int(0) ["file":"Swoft\Http\Message\Upload\UploadedFile":private]=> string(25) "/tmp/swoole.upfile.f7p2EL" ["clientFilename":"Swoft\Http\Message\Upload\UploadedFile":private]=> string(6) "at.png" ["clientMediaType":"Swoft\Http\Message\Upload\UploadedFile":private]=> string(9) "image/png" ["moved":"Swoft\Http\Message\Upload\UploadedFile":private]=> NULL ["path":"Swoft\Http\Message\Upload\UploadedFile":private]=> NULL } } 
 
          

若表單中是一個字段數組上傳多個文件如 file[] 則返回的是一個二維數組,數組內容依然是 Swoft\Http\Message\Upload\UploadedFile 文件對象,數據結構如下

 
          
array(1) { ["file"]=> array(2) { [0]=> object(Swoft\Http\Message\Upload\UploadedFile)#6516 (7) { ["size":"Swoft\Http\Message\Upload\UploadedFile":private]=> int(1319) ["errorCode":"Swoft\Http\Message\Upload\UploadedFile":private]=> int(0) ["file":"Swoft\Http\Message\Upload\UploadedFile":private]=> string(25) "/tmp/swoole.upfile.TVKdOS" ["clientFilename":"Swoft\Http\Message\Upload\UploadedFile":private]=> string(6) "at.png" ["clientMediaType":"Swoft\Http\Message\Upload\UploadedFile":private]=> string(9) "image/png" ["moved":"Swoft\Http\Message\Upload\UploadedFile":private]=> NULL ["path":"Swoft\Http\Message\Upload\UploadedFile":private]=> NULL } ... } } 
 
          

文件操作方法

 
          
  • moveTo() 將上傳的文件移動到新位置。
  • getSize() 獲取文件大小,單位 byte
  • getError() 獲取上傳文件相關的錯誤信息,若無錯將必須返回 UPLOAD_ERR_OK 常量,若又錯誤將返回 UPLOAD_ERR_XXX 相關常量。
  • getClientFilename() 獲取文件上傳時客戶端本地的文件名,不要相信此方法返回的值。客戶端可能會發送惡意虛假文件名,意圖破壞或破解您的應用程序。
  • getClientMediaType() 獲取客戶端中文件的 MediaType 類型,不要相信此方法返回的值。客戶端可能會發送惡意虛假文件名,意圖破壞或破解您的應用程序。
 
          
 
          

其他輔助方法

 
          
if ($request->isAjax()) { // Do something } if ($request->isXmlHttpRequest()) { // Do something } if ($request->isGet()) { // Do something } if ($request->isPost()) { // Do something } if ($request->isPut()) { // Do something } if ($request->isDelete()) { // Do something } if ($request->isPatch()) { // Do something } $contentType = $request->getContentType(); 
 
          

(3):Http 響應對象

 
          

Swoft 的請求與響應實現於 PSR-7 規范。請求與響應對象存在於每次 HTTP 請求。

 
          
  • 請求對象 RequestSwoft\Http\Message\Request
  • 響應對象 ResponseSwoft\Http\Message\Response
 
          

PSR-7 接口為請求和響應對象提供了這些公共方法:

 
          
  • withProtocolVersion($version)
  • withHeader($name, $value)
  • withAddedHeader($name, $value)
  • withoutHeader($name)
  • withBody(StreamInterface $body)
 
          

PSR-7 接口為響應對象提供了這些方法:

 
          
  • withStatus($code, $reasonPhrase = '')
 
          

更多請參考 PSR-7 和 查看 swoft/http-message 中具體的實現類

 
          
****警告****根據 PSR-7 對象的不可變性(immutable),所有的 with* 方法都是克隆對象然后返回,必須接收新對象來做進一步處理,或使用鏈式調用

獲取響應對象

  • 通過控制器方法參數注入 (Response $response)
  • 通過請求上下文獲取 context()->getResponse()
  • 通過請求上下文獲取 Swoft\Context\Context::mustGet()->getResponse() _(已廢棄)_
 
          

示例: 設置響應狀態碼

 
          
$response = \context()->getResponse(); return $response->withStatus(404); 
 
          

示例: 輸出字符串內容響應

 
          
return $response->withContent("Hello Swoft2.0"); 
 
          

示例: 輸出數組內容響應

 
          
$data = ['name'=>'Swoft2.0']; $response->withData($data); 
 
          

示例: 設置響應頭信息

 
          
return $response->withHeader("name","Swoft2.0");


(4):中間件
 
          

  中間件是用於控制 請求到達響應請求 的整個流程的,通常用於對請求進行過濾驗證處理,當你需要對請求或響應作出對應的修改或處理,或想調整請求處理的流程時均可以使用中間件來實現。

 
          

  定義中間件

 
          

  只需要實現了 Swoft\Http\Server\Contract\MiddlewareInterface 接口均為一個合法的中間件,其中 process() 方法為該中間件邏輯處理方法。

  
namespace App\Http\Middleware; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Http\Server\Contract\MiddlewareInterface; /** * @Bean() */ class ControllerMiddleware implements MiddlewareInterface { /** * Process an incoming server request. * * @param ServerRequestInterface $request * @param RequestHandlerInterface $handler * * @return ResponseInterface * @inheritdoc */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $response = $handler->handle($request); return $response; } }

配置全局中間件

 
          

當你的自定義中間件需要全局請求應用,則可以考慮將此中間件作為全局中間件去使用,只需在 Bean 配置文件內配置 httpDispatchermiddlewares 屬性,在數組中加入你的自定義中間件的命名空間地址,相關配置通常在 app/bean.php 內。

 
          
return [ ... 'httpDispatcher'=>[ 'middlewares'=>[ AuthMiddleware::class, ApiMiddleware::class ] ] ... ]

通過注解使用

通過 @Middleware@Middlewares, 可以很方便的配置中間件到當前的 ControllerAction 內。

  • 當將此注解應用於 Controller 上,則作用域為整個 Controller
  • 將此注解應用於 Action 上,則作用域僅為當前的 Action
  • @Middleware 用於配置單個中間件
  • @Middlewares 是用於配置一組 @Middleware,按照定義順序依次執行
 
          
namespace App\Http\Controller; use App\Http\Middleware\ApiMiddleware; use App\Http\Middleware\IndexMiddleware; use App\Http\Middleware\ControllerMiddleware; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\Middleware; use Swoft\Http\Server\Annotation\Mapping\Middlewares; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; /** * @Controller() * @Middlewares({ * @Middleware(ApiMiddleware::class), * @Middleware(ControllerMiddleware::class) * }) */ class MiddlewareController { /** * @RequestMapping() * @Middleware(IndexMiddleware::class) */ public function index(){ return "MiddlewareController"; } } 
 
          

注意:記得要引入對應的中間件類

 

應用

 
          

示例: 提前攔截請求。

 
          

攔截要在 $handler->handle($request) 之前

 
          
namespace App\Http\Middleware; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Context\Context; use Swoft\Http\Server\Contract\MiddlewareInterface; /** * @Bean() */ class SomeMiddleware implements MiddlewareInterface { /** * Process an incoming server request. * @param ServerRequestInterface $request * @param RequestHandlerInterface $handler * @return ResponseInterface * @inheritdoc */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $path = $request->getUri()->getPath(); if ($path === '/favicon.ico') { $response = Context::mustGet()->getResponse(); return $response->withStatus(404); } return $handler->handle($request); } } 
 
 
          


 
 
 

示例: 跨域設置

 
          
namespace App\Http\Middleware; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Http\Server\Contract\MiddlewareInterface; /** * @Bean() */ class CorsMiddleware implements MiddlewareInterface { /** * Process an incoming server request. * @param ServerRequestInterface $request * @param RequestHandlerInterface $handler * @return ResponseInterface * @inheritdoc */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { if ('OPTIONS' === $request->getMethod()) { $response = Context::mustGet()->getResponse(); return $this->configResponse($response); } $response = $handler->handle($request); return $this->configResponse($response); } private function configResponse(ResponseInterface $response) { return $response ->withHeader('Access-Control-Allow-Origin', 'http://mysite') ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization') ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'); } } 
 
          
 
          

示例: JWT 登錄驗證

 
          
namespace App\Http\Middleware; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Context\Context; use Swoft\Http\Server\Contract\MiddlewareInterface; /** * @Bean() */ class AuthMiddleware implements MiddlewareInterface { /** * Process an incoming server request. * @param ServerRequestInterface $request * @param RequestHandlerInterface $handler * @return ResponseInterface * @inheritdoc */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // before request handle // 判斷token $token = $request->getHeaderLine("token"); $type = \config('jwt.type'); $public = \config('jwt.publicKey'); try { $auth = JWT::decode($token, $public, ['type' => $type]); $request->user = $auth->user; } catch (\Exception $e) { $json = ['code'=>0,'msg'=>'授權失敗'] $response = Context::mustGet()->getResponse(); return $response->withData($json); } $response = $handler->handle($request); return $response; // after request handle } } 
 
          

(5)異常處理

 
          

通常我們把異常類放置 app/Exception ,異常類處理器放置 app/Exception/Handler 異常分為兩部分。自定義的 Exception 異常類,異常處理類 ExceptionHandler

 
          

定義異常類

 
          

在不同應用場景下,定義不同的異常類,例如需要一個控制器拋異常的類。

 
          
namespace App\Exception; class ApiException extends \Exception { }

定義異常處理類

 
          
namespace App\Exception\Handler; use App\Exception\ApiException; use Swoft\Error\Annotation\Mapping\ExceptionHandler; use Swoft\Http\Message\Response; use Swoft\Http\Server\Exception\Handler\AbstractHttpErrorHandler; /** * @ExceptionHandler(ApiException::class) */ class ApiExceptionHandler extends AbstractHttpErrorHandler { /** * @param \Throwable $e * @param Response $response * @return Response * @throws \ReflectionException * @throws \Swoft\Bean\Exception\ContainerException */ public function handle(\Throwable $e, Response $response): Response { $data = ['code'=>-1,'msg'=>$e->getMessage()]; return $response->withData($data); } }

@ExceptionHandler 注解

 
          

異常處理程序,指定這個處理器要處理當異常,當程序拋出 ExceptionHandler 注解里有的異常時,將會自動執行 handle 方法。

 
          
  • 指定異常:參數可以是字符串也可以是數組
    • 示例: 處理一個異常 @ExceptionHandler(ApiException::class)
    • 示例: 處理多個異常 @ExceptionHandler({ApiException::class,ServiceException::class})
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM