通常,很多人都是在控制器里直接調(diào)用view()方法渲染視圖文件,因為很多框架都是這么干的,于是大家都這么干了,但這種方式并不好,使得view與controller極度耦合。
webman中的controller沒有傳遞request對象,使得代碼寫起來有些繁瑣,基本每個action要傳遞request,但如果在construct中傳遞request,就可以這么寫:
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
修改后的代碼:
protected static function getCallback(string $plugin, string $app, $call, array $args = null, bool $withGlobalMiddleware = true, RouteObject $route = null)
{
$args = $args === null ? null : array_values($args);
$middlewares = [];
if ($route) {
$routeMiddlewares = array_reverse($route->getMiddleware());
foreach ($routeMiddlewares as $className) {
$middlewares[] = [$className, 'process'];
}
}
$middlewares = array_merge($middlewares, Middleware::getMiddleware($plugin, $app, $withGlobalMiddleware));
foreach ($middlewares as $key => $item) {
$middleware = $item[0];
if (is_string($middleware)) {
$middleware = static::container($plugin)->get($middleware);
} elseif ($middleware instanceof Closure) {
$middleware = call_user_func($middleware, static::container($plugin));
}
if (!$middleware instanceof MiddlewareInterface) {
throw new InvalidArgumentException('Not support middleware type');
}
$middlewares[$key][0] = $middleware;
}
$needInject = static::isNeedInject($call, $args);
if (is_array($call) && is_string($call[0])) {
$controllerReuse = static::config($plugin, 'app.controller_reuse', true);
if (!$controllerReuse) {
if ($needInject) {
$call = function ($request, ...$args) use ($call, $plugin) {
$call[0] = static::container($plugin)->make($call[0], [$request]);
if (true !== ($result = static::beforeCall($call[0], $call[1], $request))) {
return $result;
}
$reflector = static::getReflector($call);
$args = static::resolveMethodDependencies($plugin, $request, $args, $reflector);
// return $call(...$args);
return static::afterCall($call[0], $call[1], $call(...$args), $request);
};
$needInject = false;
} else {
$call = function ($request, ...$args) use ($call, $plugin) {
$call[0] = static::container($plugin)->make($call[0], [$request]);
if (true !== ($result = static::beforeCall($call[0], $call[1], $request))) {
return $result;
}
// return $call($request, ...$args);
return static::afterCall($call[0], $call[1], $call($request, ...$args), $request);
};
}
} else {
$call[0] = static::container($plugin)->get($call[0]);
}
}
if ($needInject) {
$call = static::resolveInject($plugin, $call);
}
if ($middlewares) {
$callback = array_reduce($middlewares, function ($carry, $pipe) {
return function ($request) use ($carry, $pipe) {
try {
return $pipe($request, $carry);
} catch (Throwable $e) {
return static::exceptionResponse($e, $request);
}
};
}, function ($request) use ($call, $args) {
try {
if ($args === null) {
$response = $call($request);
} else {
$response = $call($request, ...$args);
}
} catch (Throwable $e) {
return static::exceptionResponse($e, $request);
}
if (!$response instanceof Response) {
if (!is_string($response)) {
$response = static::stringify($response);
}
$response = new Response(200, [], $response);
}
return $response;
});
} else {
if ($args === null) {
$callback = $call;
} else {
$callback = function ($request) use ($call, $args) {
return $call($request, ...$args);
};
}
}
return $callback;
}
protected static function beforeCall($controller, $action, $request)
{
if (false !== ($result = static::detectHalted($controller))) {
return $result;
}
static::doBeforeAction($controller, $action, $request);
if (false !== ($result = static::detectHalted($controller))) {
return $result;
}
return true;
}
protected static function afterCall($controller, $action, $response, $request)
{
$method = 'after' . ucfirst($action) . 'Action';
if (method_exists($controller, $method)) {
$response = $controller->{$method}($response, $request);
}
if (method_exists($controller, 'afterAction')) {
$response = $controller->afterAction($response, $request);
}
return $response;
}
protected static function detectHalted($controller)
{
if (method_exists($controller, 'halted') && $controller->halted()) {
return $controller->getHaltedResult();
}
return false;
}
protected static function doBeforeAction($controller, $action, $request)
{
if (method_exists($controller, 'beforeAction')) {
$controller->beforeAction($request);
}
$method = 'before' . ucfirst($action) . 'Action';
if (method_exists($controller, $method)) {
$controller->{$method}($request);
}
}
食用示例:
namespace app\controller;
use Webman\App;
use support\Model;
use support\Request;
use support\Response;
/**
* 基礎(chǔ)控制器
*/
class Base
{
protected $halted = false; //標(biāo)識是否中斷
protected $haltedResult = null; //接收中斷返回的內(nèi)容
protected $request = null;
public function __construct(Request $request)
{
$this->request = $request;
}
public function halted($flag = null)
{
if (is_bool($flag)) {
$this->halted = $flag;
return;
}
return $this->halted;
}
public function getHaltedResult()
{
return $this->haltedResult;
}
// 統(tǒng)一處理action返回的結(jié)果
public function afterAction($data)
{
$script = $this->request->script;
if (!$data) {
return view($script, $data);
}
return $data;
}
}
基類對很多人來說都是有必要的,我相信大多數(shù)人都會寫基類,我提供的這個基類只是一小部分。另外,可能還會需要兩個以上基類。
在__construct()中傳遞request是為了代碼封裝,有時候把一部分業(yè)務(wù)邏輯放__construct()中處理比較方便,當(dāng)然,我也認(rèn)為$this->這樣反復(fù)寫也很累,只是建議多提供一種方式。
中間件能解決類似裝飾器模式的問題,但有時也很繁瑣,如果業(yè)務(wù)邏輯比較復(fù)雜,可能需要寫一堆中間件,其實如果能交給控制器本身處理,反而簡單明了。另外,中間件很難處理控制器里的業(yè)務(wù)邏輯,因為中間件只是簡單的before和after,但如果在控制器里,就可以方便的控制了。
1、webman也可以像你說的這樣干,但是webman不能強(qiáng)制這樣做
2、寫個基類構(gòu)造函數(shù)就解決了,但是為了控制器復(fù)用,webman不能強(qiáng)制這樣做
3、中間件可以重寫整個控制器處理邏輯,不僅僅簡單的before after
客戶端提交數(shù)據(jù)時,后端如何驗證數(shù)據(jù)?是放中間件?那不是要寫很多中間件?在我自己的框架中,我通常是放beforeAction(全局)或beforeXxxAction(某個),驗證數(shù)據(jù)放控制器里簡單明了,中間件我只用于通用性比較強(qiáng)的業(yè)務(wù)邏輯。
之前寫過beforeAction afterAction的插件,beforeAction afterAction是定義在控制器中的,用中間件實現(xiàn)的。
這個不好在內(nèi)核實現(xiàn),影響性能
http://m.wtbis.cn/plugin/30
客戶端提交數(shù)據(jù)時,后端如何驗證數(shù)據(jù)?自建個validate文件夾在里面處理,walkor大佬可以整個像fastapi框架那要快速就好了。用習(xí)慣了java每次請求接口類型都要自己判斷很不爽……
經(jīng)過再三考慮,僅非復(fù)用模式下對__construct()方法加上request參數(shù),before,after相關(guān)方法移入中間件實現(xiàn),但移植后發(fā)現(xiàn)問題,原來是框架強(qiáng)制返回了Response對象,這個還好處理,還有一個比較重要問題,那就是中間件的controller不是原來的controller,是重新new了一個,查了源碼,應(yīng)該還需要修改Container類,另起一片文章討論。
我說下我看法,不一定對。
1、把
view()
json()
xml()
redirect()
等函數(shù)獨立出來我覺得是比較解耦的做法,可以在控制器里使用,也可以在非控制器例如隊列里發(fā)送郵件時需要調(diào)用模版時使用。如果你的意思是要把這些函放在控制器基類里,那么當(dāng)你使用這些函數(shù)時就必須繼承基類并且初始化一個控制器才能使用。當(dāng)然如果喜歡這么用直接自己創(chuàng)建一個基類就可以了。
2、關(guān)于使用
$this->request
還是傳遞$request
,我覺得每次使用request都要打一遍$this->
也很繁瑣。如果不喜歡使用參數(shù)$request
可以使用函數(shù)request()
也可以省略參數(shù)少打很多字。還有如果控制器里存儲
$request
這種狀態(tài)數(shù)據(jù)就無法復(fù)用控制器實例,開啟控制器復(fù)用性能會有一定的提升,框架為了兼容兩種方式不能將$request
強(qiáng)制存儲在控制器屬性里。當(dāng)然如果喜歡這樣用就可以像類似你說的寫一個基類將
$request
存儲在控制屬性里。3、中間件做的就是攔截請求和響應(yīng)的,所以你說的beforeAction和afterAction可以用中間件直接來做。
參考 http://m.wtbis.cn/plugin/30
你上面說的這些都可以很方便的在webman上改造,但是webman框架不能強(qiáng)制做這些。