Laravel 13.40 เพิ่ม insertAfter() และ insertBefore() middleware

Laravel 13.40 เพิ่ม insertBefore() กับ insertAfter() ให้วาง middleware ตรงกลาง stack ได้โดยไม่ต้องเขียน web group ใหม่ทั้งชุด เช่นตั้ง locale หลัง StartSession หรือบล็อก IP หลัง TrustProxies แต่ถ้าตัวที่ระบุไม่อยู่ใน stack Laravel จะข้ามไปเงียบ ๆ

เผยแพร่
เวลาอ่าน
3 นาที
สรุปจาก
Paul Redmond · laravel-news.com
แท็ก
#HTTP#Laravel
ในโพสต์นี้ 6 หัวข้อ
  1. ลำดับกำหนดว่า middleware แต่ละตัวเห็นอะไรได้บ้าง
  2. เว็บสองภาษาต้องตั้ง locale ก่อน Laravel หา model
  3. insertAfterInGroup() แทรกเข้ากลาง group ได้เลย
  4. Middleware ที่ใช้ IP ผู้ใช้ต้องรันหลัง TrustProxies
  5. replace() รับ array ได้แล้ว
  6. Laravel ข้ามไปเงียบ ๆ เมื่อหาตัวที่ระบุไม่เจอ

เรามี append กับ prepend ไว้เพิ่ม middleware ใน bootstrap/app.php มาตั้งแต่ Laravel 11 สองตัวนี้วางได้แค่หัวกับท้ายของ stack แต่บางงานต้องอยู่ตรงกลางพอดี

ลำดับกำหนดว่า middleware แต่ละตัวเห็นอะไรได้บ้าง

web group เรียงตามลำดับนี้

  1. EncryptCookies
  2. AddQueuedCookiesToResponse
  3. StartSession
  4. ShareErrorsFromSession
  5. PreventRequestForgery
  6. SubstituteBindings

ตัวที่ 3 เปิดให้อ่าน session ได้ ส่วนตัวที่ 6 คือจังหวะที่ Laravel แปลงค่าใน URL เป็น model ถ้า middleware ของเราต้องอ่าน session และต้องส่งผลกับการหา model ด้วย ก็ต้องอยู่ระหว่างตัวที่ 3 กับตัวที่ 6

เว็บสองภาษาต้องตั้ง locale ก่อน Laravel หา model

สมมติเว็บขายของออนไลน์มีทั้งหน้าไทยและอังกฤษ เก็บ slug ของสินค้าแยกตามภาษาใน JSON แล้วให้ route model binding หาตามภาษาปัจจุบัน

public function resolveRouteBinding($value, $field = null)
{
    return $this->where('slug->'.app()->getLocale(), $value)->firstOrFail();
}

ทีนี้ลองเพิ่ม middleware ที่อ่านภาษาจาก session ด้วยวิธีเดิมสองแบบ

  • web(append: …) ทำงานหลัง SubstituteBindings ซึ่งหาสินค้าด้วยภาษาเริ่มต้นไปแล้ว คนที่เลือกภาษาอื่นจึงเจอ 404
  • web(prepend: …) ทำงานก่อน StartSession แล้วพังด้วย Session store not set on request.

ที่ผ่านมาเราก็เลยต้องเขียน web group ใหม่ทั้งชุดด้วย $middleware->group() ก็ใช้ได้นะ แต่ชุดที่ก็อปไว้จะไม่ตาม framework เช่น Laravel 12 ใช้ ValidateCsrfToken ส่วน 13 เปลี่ยนเป็น PreventRequestForgery และ auth.session จาก authenticateSessions() ก็จะหายไปด้วย

insertAfterInGroup() แทรกเข้ากลาง group ได้เลย

use App\Http\Middleware\SetLocaleFromSession;
use Illuminate\Session\Middleware\StartSession;

->withMiddleware(function (Middleware $middleware) {
    $middleware->insertAfterInGroup('web', StartSession::class, SetLocaleFromSession::class);
})

บรรทัดเดียวนี้ทำให้ middleware ไปอยู่ถัดจาก StartSession ส่วนตัวอื่นยังเป็นไปตามที่ framework กำหนด อัปเกรด Laravel รอบหน้าเราก็ไม่ต้องตามแก้

เมธอด ใช้กับ ลำดับอาร์กิวเมนต์
insertBefore(), insertAfter() global stack ตัวที่หา, ตัวที่จะแทรก
insertBeforeInGroup(), insertAfterInGroup() group ใดก็ได้ รวมถึง group ที่สร้างเอง ชื่อ group, ตัวที่หา, ตัวที่จะแทรก

ตัวที่จะแทรกส่งเป็น array ได้ ถ้ามีหลายตัว

Middleware ที่ใช้ IP ผู้ใช้ต้องรันหลัง TrustProxies

global stack ก็เจอแบบเดียวกัน ถ้าเว็บอยู่หลัง Cloudflare หรือ load balancer แล้วเราจะบล็อก IP ที่ติด blacklist

  • prepend() รันก่อน TrustProxies ซึ่ง $request->ip() ยังเป็น IP ของ proxy
  • append() ได้ IP ถูกแล้ว แต่รันหลัง HandleCors, ตัวเช็ค maintenance mode และตัวจัดการ input

insertAfter() วางไว้ถัดจาก TrustProxies พอดี

use App\Http\Middleware\BlockBannedIps;
use Illuminate\Http\Middleware\TrustProxies;

$middleware->insertAfter(TrustProxies::class, BlockBannedIps::class);

เรื่องนี้ต้องตั้ง trustProxies() ให้ถูกก่อนด้วย ไม่อย่างนั้น IP ที่ได้ก็ยังเชื่อไม่ได้ อ่านวิธีตั้งได้ที่ตั้ง trustProxies ให้ Laravel ที่อยู่หลัง Cloudflare

ส่วน insertBefore() ใช้แบบเดียวกัน เช่นทำความสะอาด input ก่อน TrimStrings

use App\Http\Middleware\SanitizeInput;
use Illuminate\Foundation\Http\Middleware\TrimStrings;

$middleware->insertBefore(TrimStrings::class, SanitizeInput::class);

replace() รับ array ได้แล้ว

13.40 ให้ replace() กับ replaceInGroup() แทนที่ตัวเดียวด้วยหลายตัวได้ เหมาะเมื่อจะเปลี่ยนตัวเดิมเป็นสองตัวใหม่

$middleware->replace(Foo::class, [Bar::class, Baz::class]);

ถ้าแค่อยากแทรก ใช้ insert… จะอ่านตรงกว่าการใส่ตัวเดิมกลับเข้าไปใน array

Laravel ข้ามไปเงียบ ๆ เมื่อหาตัวที่ระบุไม่เจอ

เมธอดกลุ่มนี้ไม่มีขั้นตอนของตัวเอง โค้ดของ Laravel เก็บไว้เป็นรายการ replace แล้วค่อยไล่เทียบกับ stack ตอนประกอบ ผลที่ตามมาคือ

  • หาไม่เจอก็ไม่มี error middleware ที่จะแทรกจะไม่เข้า stack เลย
  • ต้องตรงทุกตัวอักษร ชื่อ class เทียบกับ alias อย่าง auth.session ไม่ได้ และ subclass ก็ไม่นับเป็น parent
  • ใช้ตัวที่เพิ่งเพิ่มเป็นจุดอ้างอิงไม่ได้ ตัวที่มาจาก append, prepend หรือ insert… อื่นจะหาไม่เจอ insertAfter(A, B) ต่อด้วย insertAfter(B, C) จึงไม่มี C ใน stack ให้เขียนเป็น insertAfter(A, [B, C]) แทน
  • replace() ที่เรียกทีหลังจะทับ การแทรกก่อนหน้าที่อ้างตัวเดียวกัน

วิธีเช็คคือเปิดดู stack ของ route จริงหลังแก้ทุกครั้ง

php artisan route:list -vv --path=products

-vv กาง group ออกให้เห็นทีละตัว แต่ไม่แสดง global stack ถ้าแทรกในระดับ global ให้เขียน feature test ยิง request ดูผลแทน เช่นยิงจาก IP ที่บล็อกแล้วต้องได้ 403

ฟีเจอร์นี้ต้องใช้ Laravel 13.40 ขึ้นไป ตัวอย่างและรายละเอียดเพิ่มเติมอยู่ในโพสต์ต้นฉบับของ Laravel News ครับ

อ่านต่อ

ล่าสุด

ดูทั้งหมด →