Ara Katman
Ara katmanlar genellikle istekleri veya yanıtları engellemek için kullanılır. Örneğin, kontrolcü çalıştırılmadan önce kullanıcı kimliğini doğrulamak, kullanıcı giriş yapmamışsa giriş sayfasına yönlendirmek, yanıt içinde belirli bir başlık eklemek gibi. Ayrıca belirli bir URI isteğinin oranını istatistiksel olarak hesaplamak gibi işlemler de yapılabilir.
Ara Katman Soğan Modeli
┌──────────────────────────────────────────────────────┐
│ middleware1 │
│ ┌──────────────────────────────────────────┐ │
│ │ middleware2 │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ middleware3 │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ │ │ │ │
── İstek ───────────────────> Kontrolcü ── Yanıt ───────────────────────────> İstemci
│ │ │ │ │ │ │ │
│ │ │ └──────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘
Ara katmanlar ve kontrolcü, klasik bir soğan modeli oluşturur; ara katmanlar, bir katman katman soğan kabuğu gibi, kontrolcü ise soğan çekirdeğidir. Şekilde gösterildiği gibi, istek, ara katman 1, 2, 3 üzerinden kontrolöre ulaşır, kontrolör bir yanıt döndürür ve daha sonra yanıt, ara katmanlardan 3, 2, 1 sırasıyla çıkarak nihayet istemciye döner. Yani her bir ara katmanda hem isteği elde edebiliriz hem de yanıtı alabiliriz.
İstek Engelleme
Bazen bir isteğin kontrolör katmanına ulaşmasını istemeyiz; örneğin, middleware2'de mevcut kullanıcının giriş yapmadığını tespit edersek, isteği doğrudan kesip bir giriş yanıtı dönebiliriz. Bu işlem aşağıdaki gibidir.
┌────────────────────────────────────────────────────────────┐
│ middleware1 │
│ ┌────────────────────────────────────────────────┐ │
│ │ middleware2 │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ middleware3 │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ │ │ │ │
── İstek ─────────┐ │ │ Kontrolcü │ │ │ │
│ │ Yanıt │ │ │ │ │ │
<───────────────────┘ │ └──────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────┘
Yukarıdaki şekilde, istek middleware2'ye ulaştığında bir giriş yanıtı oluşturulur, yanıt middleware2'den middleware1'e geçerek istemciye döner.
Ara Katman Arayüzü
Ara katmanlar, Webman\MiddlewareInterface
arayüzünü uygulamalıdır.
interface MiddlewareInterface
{
/**
* Gelen bir sunucu isteğini işleme.
*
* Gelen bir sunucu isteğini işleyerek bir yanıt üretir.
* Yanıtı kendisi üretemezse, sağlanan istek yöneticisine devredebilir.
*/
public function process(Request $request, callable $handler): Response;
}
Bu, process
yönteminin uygulanması gerektiği anlamına gelir; process
yöntemi, bir support\Response
nesnesi döndürmelidir. Varsayılan olarak, bu nesne $handler($request)
tarafından üretilir (istek, soğan çekirdeği boyunca devam eder), yanıt response()
, json()
, xml()
, redirect()
gibi yardımcı fonksiyonlar tarafından oluşturulmuş bir yanıt da olabilir (istek, soğan çekirdeği boyunca devam etmez).
Ara Katman İçinde İsteği ve Yanıtı Alma
Ara katman içinde hem isteği hem de kontrolörün ardından geri dönen yanıtı alabiliriz, bu nedenle ara katman içi üç bölüme ayrılır.
- İsteğin geçiş aşaması, yani isteğin işlenmeden önceki aşaması
- Kontrolörün isteği işlediği aşama, yani isteğin işlenme aşaması
- Yanıtın çıkış aşaması, yani isteğin işlendiği sonraki aşama
Bu üç aşamanın ara katmandaki gösterimi şu şekildedir.
<?php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class Test implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
echo 'Burada istek geçiş aşaması, yani istek işlenmeden önce';
$response = $handler($request); // Soğan çekirdeği boyunca devam eder, kontrolör çalıştırılır ve yanıt alınır
echo 'Burada yanıt çıkış aşaması, yani istek işlendiğinde';
return $response;
}
}
Örnek: Kimlik Doğrulama Ara Katmanı
app/middleware/AuthCheckTest.php
dosyasını oluşturun (dizin yoksa kendiniz oluşturun) aşağıdaki gibi:
<?php
namespace app\middleware;
use ReflectionClass;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class AuthCheckTest implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
if (session('user')) {
// Zaten giriş yapmış, istek soğan çekirdeği boyunca devam eder
return $handler($request);
}
// Yansıma ile kontrolörde hangi yöntemlerin giriş yapmadan erişilemeyeceğini al
$controller = new ReflectionClass($request->controller);
$noNeedLogin = $controller->getDefaultProperties()['noNeedLogin'] ?? [];
// Erişilen yöntem giriş yapmayı gerektiriyor
if (!in_array($request->action, $noNeedLogin)) {
// İsteği kes, bir yönlendirme yanıtı döndür, istek soğan çekirdeği boyunca devam etmesin
return redirect('/user/login');
}
// Giriş yapılmasına gerek yok, istek soğan çekirdeği boyunca devam eder
return $handler($request);
}
}
Yeni bir kontrolör oluşturun app/controller/UserController.php
<?php
namespace app\controller;
use support\Request;
class UserController
{
/**
* Giriş yapılmasına gerek olmayan yöntem
*/
protected $noNeedLogin = ['login'];
public function login(Request $request)
{
$request->session()->set('user', ['id' => 10, 'name' => 'webman']);
return json(['code' => 0, 'msg' => 'giriş ok']);
}
public function info()
{
return json(['code' => 0, 'msg' => 'ok', 'data' => session('user')]);
}
}
Dikkat
$noNeedLogin
, mevcut kontrolörün giriş yapılmadan erişilebilecek yöntemlerini kaydeder.
config/middleware.php
dosyasına aşağıdaki gibi küresel ara katman ekleyin:
return [
// Küresel ara katmanlar
'' => [
// ... Burada diğer ara katmanlar atlanmış
app\middleware\AuthCheckTest::class,
]
];
Kimlik doğrulama ara katmanına sahip olduğumuzda, kontrolör katmanında iş kodumuzu rahatlıkla yazabiliriz ve kullanıcı giriş yapıp yapmadığı nedeniyle endişelenmeyiz.
Örnek: Çapraz Alan İsteği Ara Katmanı
app/middleware/AccessControlTest.php
dosyasını oluşturun (dizin yoksa kendiniz oluşturun) aşağıdaki gibi:
<?php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class AccessControlTest implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
// Eğer istek bir options ise boğ bir yanıt döndür, aksi takdirde soğan çekirdeği boyunca devam et ve bir yanıt al
$response = $request->method() == 'OPTIONS' ? response('') : $handler($request);
// Yanıta çapraz alan ile ilgili http başlıkları ekle
$response->withHeaders([
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Origin' => $request->header('origin', '*'),
'Access-Control-Allow-Methods' => $request->header('access-control-request-method', '*'),
'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', '*'),
]);
return $response;
}
}
İpucu
Çapraz alan, OPTIONS isteği üretebilir; OPTIONS isteğinin kontrolöre girmesini istemediğimiz için, OPTIONS isteği için doğrudan boş bir yanıt döndürdük (response('')
) ve böylece istek kesildi.
Eğer arayüzünüz yönlendirme ayarları gerektiriyorsa,Route::any(..)
veyaRoute::add(['POST', 'OPTIONS'], ..)
kullanarak ayarlayınız.
config/middleware.php
dosyasına aşağıdaki gibi küresel ara katman ekleyin:
return [
// Küresel ara katmanlar
'' => [
// ... Burada diğer ara katmanlar atlanmış
app\middleware\AccessControlTest::class,
]
];
Dikkat
Eğer ajax isteği özel başlık tanımlıyorsa, ara katmandaAccess-Control-Allow-Headers
alanına bu özel başlık eklenmelidir, aksi takdirdeRequest header field XXXX is not allowed by Access-Control-Allow-Headers in preflight response.
hatası alırsınız.
Açıklama
- Ara katmanlar, küresel ara katmanlar, uygulama ara katmanları (uygulama ara katmanları yalnızca çoklu uygulama modunda etkilidir, Çoklu Uygulama kısmına bakınız) ve yönlendirme ara katmanları olarak ayrılabilir.
- Ara katman ayar dosyası
config/middleware.php
dosyasında bulunur. - Küresel ara katman ayarları
''
anahtarında yapılandırılır. - Uygulama ara katmanları belirli bir uygulama adı altında yapılandırılır, örneğin:
return [
// Küresel ara katmanlar
'' => [
app\middleware\AuthCheckTest::class,
app\middleware\AccessControlTest::class,
],
// api uygulama ara katmanları (uygulama ara katmanları yalnızca çoklu uygulama modunda etkilidir)
'api' => [
app\middleware\ApiOnly::class,
]
];
Kontrolör Ara Katmanları ve Metot Ara Katmanları
Açıklamalar aracılığıyla belirli bir kontrolöre veya kontrolörün belirli bir metoduna ara katmanlar ayarlayabiliriz.
<?php
namespace app\controller;
use app\middleware\Controller1Middleware;
use app\middleware\Controller2Middleware;
use app\middleware\Method1Middleware;
use app\middleware\Method2Middleware;
use support\annotation\Middleware;
use support\Request;
#[Middleware(Controller1Middleware::class, Controller2Middleware::class)]
class IndexController
{
#[Middleware(Method1Middleware::class, Method2Middleware::class)]
public function index(Request $request): string
{
return 'merhaba';
}
}
Yönlendirme Ara Katmanları
Belirli bir yol veya bir grup yola ara katmanlar atayabiliriz.
Örneğin, config/route.php
dosyasına aşağıdaki yapılandırmayı ekleyin:
<?php
use support\Request;
use Webman\Route;
Route::any('/admin', [app\admin\controller\IndexController::class, 'index'])->middleware([
app\middleware\MiddlewareA::class,
app\middleware\MiddlewareB::class,
]);
Route::group('/blog', function () {
Route::any('/create', function () {return response('oluştur');});
Route::any('/edit', function () {return response('düzenle');});
Route::any('/view/{id}', function ($r, $id) {response("görünüm $id");});
})->middleware([
app\middleware\MiddlewareA::class,
app\middleware\MiddlewareB::class,
]);
Ara Katman Yapılandırıcısına Parametre Geçişi
Ayar dosyaları, ara katmanları doğrudan örneklendirmenize veya anonim fonksiyonlar kullanmanıza izin verir; böylece yapılandırıcı aracılığıyla ara katmanlara parametre geçişi yapabilirsiniz. Örneğin, config/middleware.php
dosyasında aşağıdaki gibi de yapılandırılabilir.
return [
// Küresel ara katmanlar
'' => [
new app\middleware\AuthCheckTest($param1, $param2, ...),
function(){
return new app\middleware\AccessControlTest($param1, $param2, ...);
},
],
// api uygulama ara katmanları (uygulama ara katmanları yalnızca çoklu uygulama modunda etkilidir)
'api' => [
app\middleware\ApiOnly::class,
]
];
Benzer şekilde, yönlendirme ara katmanları, yapılandırıcı aracılığıyla ara katmanlara parametre geçirebilir, örneğin şu şekilde config/route.php
dosyasında:
Route::any('/admin', [app\admin\controller\IndexController::class, 'index'])->middleware([
new app\middleware\MiddlewareA($param1, $param2, ...),
function(){
return new app\middleware\MiddlewareB($param1, $param2, ...);
},
]);
Ara katman içinde parametre kullanımı örneği:
<?php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class MiddlewareA implements MiddlewareInterface
{
protected $param1;
protected $param2;
public function __construct($param1, $param2)
{
$this->param1 = $param1;
$this->param2 = $param2;
}
public function process(Request $request, callable $handler) : Response
{
var_dump($this->param1, $this->param2);
return $handler($request);
}
}
Ara Katmanların Çalıştırılma Sırası
- Ara katmanların yürütülme sırası
küresel ara katman
->uygulama ara katman
->kontrolör ara katman
->yönlendirme ara katman
->metot ara katman
şeklindedir. - Aynı katmanda birden fazla ara katman varsa, aynı seviyedeki gerçek yapılandırma sırasına göre çalışır.
- 404 talepleri varsayılan olarak herhangi bir ara katmanı tetiklemez (ancak yine de
Route::fallback(function(){})->middleware()
ekleyerek ara katman ekleyebilirsiniz).
Yönlendirme Ara Katmanı Parametre Geçişi (route->setParams)
Yönlendirme yapılandırması config/route.php
<?php
use support\Request;
use Webman\Route;
Route::any('/test', [app\controller\IndexController::class, 'index'])->setParams(['some_key' =>'some value']);
Ara katman (varsayılan olarak küresel ara katman)
<?php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class Hello implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
// Varsayılan yönlendirme $request->route olarak null; dolayısıyla $request->route'nın boş olup olmadığını kontrol etmek gerekir.
if ($route = $request->route) {
$value = $route->param('some_key');
var_export($value);
}
return $handler($request);
}
}
Ara Katmanlar ile Kontrolöre Parametre Geçişi
Bazen kontrolör, ara katmanda oluşturulan verilere ihtiyaç duyar, bu durumda $request
nesnesine özellik ekleyerek kontrolöre parametre geçebiliriz. Örneğin:
Ara katman
<?php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class Hello implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
$request->data = 'bazı değer';
return $handler($request);
}
}
Kontrolör:
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function index(Request $request)
{
return response($request->data);
}
}
Ara Katmanlarla Mevcut İstek Yönlendirme Bilgilerini Alma
$request->route
aracılığıyla yönlendirme nesnesini alabiliriz ve ilgili bilgiler almak için uygun yöntemleri çağırabiliriz.
Yönlendirme yapılandırması
<?php
use support\Request;
use Webman\Route;
Route::any('/user/{uid}', [app\controller\UserController::class, 'view']);
Ara katman
<?php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class Hello implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
$route = $request->route;
// İstek hiçbir yönlendirmeye uymuyorsa (varsayılan yönlendirme dışında), $request->route null olur
//Tarayıcı /user/111 adresine erişirse, aşağıdaki bilgiler yazdırılacaktır.
if ($route) {
var_export($route->getPath()); // /user/{uid}
var_export($route->getMethods()); // ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD','OPTIONS']
var_export($route->getName()); // user_view
var_export($route->getMiddleware()); // []
var_export($route->getCallback()); // ['app\\controller\\UserController', 'view']
var_export($route->param()); // ['uid'=>111]
var_export($route->param('uid')); // 111
}
return $handler($request);
}
}
Dikkat
Ara Katmanla Hataları Alma
İşlem sırasında hatalar oluşabilir, ara katmanda $response->exception()
kullanarak hataları alabilirsiniz.
Yönlendirme yapılandırması
<?php
use support\Request;
use Webman\Route;
Route::any('/user/{uid}', function (Request $request, $uid) {
throw new \Exception('istisna testi');
});
Ara katman:
<?php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class Hello implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
$response = $handler($request);
$exception = $response->exception();
if ($exception) {
echo $exception->getMessage();
}
return $response;
}
}
Süper Küresel Ara Katman
Ana projenin küresel ara katmanları yalnızca ana projeyi etkiler, uygulama eklentileri üzerinde etkisi yoktur. Bazen, tüm eklentiler de dahil olmak üzere küresel bir ara katman uygulamak isteyebiliriz, bu durumda süper küresel ara katmanları kullanabiliriz.
config/middleware.php
dosyasında aşağıdaki gibi yapılandırın:
return [
'@' => [ // Ana proje ve tüm eklentilere küresel ara katman ekleyin
app\middleware\MiddlewareGlobl::class,
],
'' => [], // Sadece ana projeye küresel ara katman ekleyin
];
İpucu
@
süper küresel ara katman yalnızca ana projede değil, aynı zamanda belirli bir eklenti içinde de yapılandırılabilir. Örneğin,plugin/ai/config/middleware.php
içinde@
süper küresel ara katman yapılandırıldığında, ana projeyi ve tüm eklentileri de etkiler.
Belirli Bir Eklentinin Ara Katmanını Artırma
Bazen belirli bir uygulama eklentisine ara katman eklemek isteyebiliriz, ancak eklentinin kodunu değiştirmek istemiyoruz (çünkü güncelleme sırasında üzerine yazılabilir), bu durumda ana projede bir ara katman yapılandırarak yapabiliriz.
config/middleware.php
dosyasında aşağıdaki gibi yapılandırın:
return [
'plugin.ai' => [], // ai eklentisine ara katman ekleyin
'plugin.ai.admin' => [], // ai eklentisinin admin modülüne (plugin\ai\app\admin dizini) ara katman ekleyin
];
İpucu
Benzer şekilde, belirli bir eklenti içinde aşağıdaki gibi yapılandırmalar ekleyerek diğer eklentilerin etkilenmesini sağlayabilirsiniz. Örneğin,plugin/foo/config/middleware.php
içinde yukarıdaki gibi bir yapılandırma eklenirse, ai eklentisini etkileyecektir.