การกำหนดเส้นทางวิธีการตามสัญญาและเลเยอร์มิดเดิลแวร์สำหรับเส้นทาง Next.js API เส้นทาง API Edge, Middleware, Next.js App เราเตอร์และ GetServersideProps
npm install next-connect@nextตรวจสอบโฟลเดอร์ตัวอย่างด้วย
next-connect สามารถใช้ในเส้นทาง API
// pages/api/user/[id].ts
import type { NextApiRequest , NextApiResponse } from "next" ;
import { createRouter , expressWrapper } from "next-connect" ;
import cors from "cors" ;
const router = createRouter < NextApiRequest , NextApiResponse > ( ) ;
router
// Use express middleware in next-connect with expressWrapper function
. use ( expressWrapper ( passport . session ( ) ) )
// A middleware example
. use ( async ( req , res , next ) => {
const start = Date . now ( ) ;
await next ( ) ; // call next in chain
const end = Date . now ( ) ;
console . log ( `Request took ${ end - start } ms` ) ;
} )
. get ( ( req , res ) => {
const user = getUser ( req . query . id ) ;
res . json ( { user } ) ;
} )
. put ( ( req , res ) => {
if ( req . user . id !== req . query . id ) {
throw new ForbiddenError ( "You can't update other user's profile" ) ;
}
const user = await updateUser ( req . body . user ) ;
res . json ( { user } ) ;
} ) ;
export const config = {
runtime : "edge" ,
} ;
export default router . handler ( {
onError : ( err , req , res ) => {
console . error ( err . stack ) ;
res . status ( err . statusCode || 500 ) . end ( err . message ) ;
} ,
} ) ; next-connect สามารถใช้ในเส้นทาง API ขอบ
// pages/api/user/[id].ts
import type { NextFetchEvent , NextRequest } from "next/server" ;
import { createEdgeRouter } from "next-connect" ;
import cors from "cors" ;
const router = createEdgeRouter < NextRequest , NextFetchEvent > ( ) ;
router
// A middleware example
. use ( async ( req , event , next ) => {
const start = Date . now ( ) ;
await next ( ) ; // call next in chain
const end = Date . now ( ) ;
console . log ( `Request took ${ end - start } ms` ) ;
} )
. get ( ( req ) => {
const id = req . nextUrl . searchParams . get ( "id" ) ;
const user = getUser ( id ) ;
return NextResponse . json ( { user } ) ;
} )
. put ( ( req ) => {
const id = req . nextUrl . searchParams . get ( "id" ) ;
if ( req . user . id !== id ) {
throw new ForbiddenError ( "You can't update other user's profile" ) ;
}
const user = await updateUser ( req . body . user ) ;
return NextResponse . json ( { user } ) ;
} ) ;
export default router . handler ( {
onError : ( err , req , event ) => {
console . error ( err . stack ) ;
return new NextResponse ( "Something broke!" , {
status : err . statusCode || 500 ,
} ) ;
} ,
} ) ; next-connect สามารถใช้ใน Next.js 13 Route Handler วิธีการเขียนตัวจัดการนั้นเกือบจะเหมือนกันกับเส้นทาง Next.js Edge API โดยใช้ createEdgeRouter
// app/api/user/[id]/route.ts
import type { NextFetchEvent , NextRequest } from "next/server" ;
import { createEdgeRouter } from "next-connect" ;
import cors from "cors" ;
interface RequestContext {
params : {
id : string ;
} ;
}
const router = createEdgeRouter < NextRequest , RequestContext > ( ) ;
router
// A middleware example
. use ( async ( req , event , next ) => {
const start = Date . now ( ) ;
await next ( ) ; // call next in chain
const end = Date . now ( ) ;
console . log ( `Request took ${ end - start } ms` ) ;
} )
. get ( ( req ) => {
const id = req . params . id ;
const user = getUser ( id ) ;
return NextResponse . json ( { user } ) ;
} )
. put ( ( req ) => {
const id = req . params . id ;
if ( req . user . id !== id ) {
throw new ForbiddenError ( "You can't update other user's profile" ) ;
}
const user = await updateUser ( req . body . user ) ;
return NextResponse . json ( { user } ) ;
} ) ;
export async function GET ( request : NextRequest , ctx : RequestContext ) {
return router . run ( request , ctx ) ;
}
export async function PUT ( request : NextRequest , ctx : RequestContext ) {
return router . run ( request , ctx ) ;
} next-connect สามารถใช้ในมิดเดิลแวร์ next.js
// middleware.ts
import { NextResponse } from "next/server" ;
import type { NextRequest , NextFetchEvent } from "next/server" ;
import { createEdgeRouter } from "next-connect" ;
const router = createEdgeRouter < NextRequest , NextFetchEvent > ( ) ;
router . use ( async ( request , event , next ) => {
// logging request example
console . log ( ` ${ request . method } ${ request . url } ` ) ;
return next ( ) ;
} ) ;
router . get ( "/about" , ( request ) => {
return NextResponse . redirect ( new URL ( "/about-2" , request . url ) ) ;
} ) ;
router . use ( "/dashboard" , ( request ) => {
if ( ! isAuthenticated ( request ) ) {
return NextResponse . redirect ( new URL ( "/login" , request . url ) ) ;
}
return NextResponse . next ( ) ;
} ) ;
router . all ( ( ) => {
// default if none of the above matches
return NextResponse . next ( ) ;
} ) ;
export function middleware ( request : NextRequest , event : NextFetchEvent ) {
return router . run ( request , event ) ;
}
export const config = {
matcher : [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
"/((?!api|_next/static|_next/image|favicon.ico).*)" ,
] ,
} ; next-connect สามารถใช้ใน GetServersideprops
// pages/users/[id].js
import { createRouter } from "next-connect" ;
export default function Page ( { user , updated } ) {
return (
< div >
{ updated && < p > User has been updated </ p > }
< div > { JSON . stringify ( user ) } </ div >
< form method = "POST" > { /* User update form */ } </ form >
</ div >
) ;
}
const router = createRouter ( )
. use ( async ( req , res , next ) => {
// this serve as the error handling middleware
try {
return await next ( ) ;
} catch ( e ) {
return {
props : { error : e . message } ,
} ;
}
} )
. get ( async ( req , res ) => {
const user = await getUser ( req . params . id ) ;
if ( ! user ) {
// https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#notfound
return { props : { notFound : true } } ;
}
return { props : { user } } ;
} )
. put ( async ( req , res ) => {
const user = await updateUser ( req ) ;
return { props : { user , updated : true } } ;
} ) ;
export async function getServerSideProps ( { req , res } ) {
return router . run ( req , res ) ;
} APIs ต่อไปนี้ถูกเขียนใหม่ในแง่ของ NodeRouter ( createRouter ) แต่พวกเขาใช้กับ EdgeRouter ( createEdgeRouter ) เช่นกัน
สร้างอินสแตนซ์ node.js เราเตอร์
base (ไม่บังคับ) - จับคู่เส้นทางทั้งหมดกับด้านขวาของ base หรือจับคู่ทั้งหมดหากถูกละเว้น (หมายเหตุ: หากใช้ใน next.js สิ่งนี้มักจะถูกละเว้น)
fn สามารถเป็นได้:
(req, res[, next]) // Mount a middleware function
router1 . use ( async ( req , res , next ) => {
req . hello = "world" ;
await next ( ) ; // call to proceed to the next in chain
console . log ( "request is done" ) ; // call after all downstream handler has run
} ) ;
// Or include a base
router2 . use ( "/foo" , fn ) ; // Only run in /foo/**
// mount an instance of router
const sub1 = createRouter ( ) . use ( fn1 , fn2 ) ;
const sub2 = createRouter ( ) . use ( "/dashboard" , auth ) ;
const sub3 = createRouter ( )
. use ( "/waldo" , subby )
. get ( getty )
. post ( "/baz" , posty )
. put ( "/" , putty ) ;
router3
// - fn1 and fn2 always run
// - auth runs only on /dashboard
. use ( sub1 , sub2 )
// `subby` runs on ANY /foo/waldo?/*
// `getty` runs on GET /foo/*
// `posty` runs on POST /foo/baz
// `putty` runs on PUT /foo
. use ( "/foo" , sub3 ) ; วิธี METHOD เป็นวิธี HTTP ( GET , HEAD , POST , PUT , PATCH , DELETE , OPTIONS , TRACE ) ในตัวพิมพ์เล็ก
pattern (ไม่บังคับ) - เส้นทางการจับคู่ตามรูปแบบที่รองรับหรือจับคู่ใด ๆ หากละเว้น
fn (s) เป็นฟังก์ชั่นของ (req, res[, next])
router . get ( "/api/user" , ( req , res , next ) => {
res . json ( req . user ) ;
} ) ;
router . post ( "/api/users" , ( req , res , next ) => {
res . end ( "User created" ) ;
} ) ;
router . put ( "/api/user/:id" , ( req , res , next ) => {
// https://nextjs.org/docs/routing/dynamic-routes
res . end ( `User ${ req . params . id } updated` ) ;
} ) ;
// Next.js already handles routing (including dynamic routes), we often
// omit `pattern` in `.METHOD`
router . get ( ( req , res , next ) => {
res . end ( "This matches whatever route" ) ;
} ) ;หมายเหตุ คุณควรเข้าใจการกำหนดเส้นทางตามระบบไฟล์ next.js ตัวอย่างเช่นการมี
router.put("/api/foo", handler)ภายในpage/api/index.jsไม่ได้ ให้บริการตัวจัดการ AT/api/foo
เช่นเดียวกับ. method แต่ยอมรับวิธีการ ใด ๆ
สร้างตัวจัดการเพื่อจัดการคำขอที่เข้ามา
ตัวเลือก
ยอมรับฟังก์ชั่นเป็นตัวจัดการข้อผิดพลาดทั้งหมด ดำเนินการเมื่อใดก็ตามที่ตัวจัดการส่งข้อผิดพลาด โดยค่าเริ่มต้นจะตอบสนองด้วย 500 Internal Server Error ทั่วไปในขณะที่บันทึกข้อผิดพลาดเป็น console
function onError ( err , req , res ) {
logger . log ( err ) ;
// OR: console.error(err);
res . status ( 500 ) . end ( "Internal server error" ) ;
}
export default router . handler ( { onError } ) ;ตัวเลือก
ยอมรับฟังก์ชั่นของ (req, res) เป็นตัวจัดการเมื่อไม่มีการจับคู่เส้นทาง โดยค่าเริ่มต้นจะตอบสนองด้วยสถานะ 404 และ Route [Method] [Url] not found ร่างกาย
function onNoMatch ( req , res ) {
res . status ( 404 ) . end ( "page is not found... or is it!?" ) ;
}
export default router . handler ( { onNoMatch } ) ; รัน req และ res ผ่านห่วงโซ่มิดเดิลแวร์และส่งคืน สัญญา มันแก้ไขได้ด้วยค่าที่ส่งคืนจากตัวจัดการ
router
. use ( async ( req , res , next ) => {
return ( await next ( ) ) + 1 ;
} )
. use ( async ( ) => {
return ( await next ( ) ) + 2 ;
} )
. use ( async ( ) => {
return 3 ;
} ) ;
console . log ( await router . run ( req , res ) ) ;
// The above will print "6" หากข้อผิดพลาดในการโยนภายในห่วงโซ่ router.run จะปฏิเสธ นอกจากนี้คุณยังสามารถเพิ่มการลองในมิดเดิลแวร์คนแรกเพื่อรับข้อผิดพลาดก่อนที่จะปฏิเสธการโทร .run() :
router
. use ( async ( req , res , next ) => {
return next ( ) . catch ( errorHandler ) ;
} )
. use ( thisMiddlewareMightThrow ) ;
await router . run ( req , res ) ; มีข้อผิดพลาดบางอย่างในการใช้ next-connect ด้านล่างนี้เป็นสิ่งที่ควรจำไว้อย่างถูกต้อง
await next() หาก next() ไม่รอข้อผิดพลาดจะไม่ถูกจับหากพวกเขาถูกโยนลงไปในตัวจัดการ Async นำไปสู่ UnhandledPromiseRejection
// OK: we don't use async so no need to await
router
. use ( ( req , res , next ) => {
next ( ) ;
} )
. use ( ( req , res , next ) => {
next ( ) ;
} )
. use ( ( ) => {
throw new Error ( "?" ) ;
} ) ;
// BAD: This will lead to UnhandledPromiseRejection
router
. use ( async ( req , res , next ) => {
next ( ) ;
} )
. use ( async ( req , res , next ) => {
next ( ) ;
} )
. use ( async ( ) => {
throw new Error ( "?" ) ;
} ) ;
// GOOD
router
. use ( async ( req , res , next ) => {
await next ( ) ; // next() is awaited, so errors are caught properly
} )
. use ( ( req , res , next ) => {
return next ( ) ; // this works as well since we forward the rejected promise
} )
. use ( async ( ) => {
throw new Error ( "?" ) ;
// return new Promise.reject("?");
} ) ;อีกประเด็นหนึ่งคือตัวจัดการจะแก้ไขก่อนที่รหัสทั้งหมดในแต่ละเลเยอร์จะทำงาน
const handler = router
. use ( async ( req , res , next ) => {
next ( ) ; // this is not returned or await
} )
. get ( async ( ) => {
// simulate a long task
await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
res . send ( "ok" ) ;
console . log ( "request is completed" ) ;
} )
. handler ( ) ;
await handler ( req , res ) ;
console . log ( "finally" ) ; // this will run before the get layer gets to finish
// This will result in:
// 1) "finally"
// 2) "request is completed"router กลับมาใช้ใหม่เช่นรูปแบบด้านล่าง: // api-libs/base.js
export default createRouter ( ) . use ( a ) . use ( b ) ;
// api/foo.js
import router from "api-libs/base" ;
export default router . get ( x ) . handler ( ) ;
// api/bar.js
import router from "api-libs/base" ;
export default router . get ( y ) . handler ( ) ; นี่เป็นเพราะในแต่ละเส้นทาง API อินสแตนซ์ของเราเตอร์เดียวกันจะกลายพันธุ์ซึ่งนำไปสู่พฤติกรรมที่ไม่ได้กำหนด หากคุณต้องการทำสิ่งต่าง ๆ เช่นนั้นคุณสามารถใช้ router.clone เพื่อส่งคืนอินสแตนซ์ที่แตกต่างกันด้วยเส้นทางเดียวกันที่มีประชากร
// api-libs/base.js
export default createRouter ( ) . use ( a ) . use ( b ) ;
// api/foo.js
import router from "api-libs/base" ;
export default router . clone ( ) . get ( x ) . handler ( ) ;
// api/bar.js
import router from "api-libs/base" ;
export default router . clone ( ) . get ( y ) . handler ( ) ;res.(s)end หรือ res.redirect ภายใน getServerSideProps // page/index.js
const handler = createRouter ( )
. use ( ( req , res ) => {
// BAD: res.redirect is not a function (not defined in `getServerSideProps`)
// See https://github.com/hoangvvo/next-connect/issues/194#issuecomment-1172961741 for a solution
res . redirect ( "foo" ) ;
} )
. use ( ( req , res ) => {
// BAD: `getServerSideProps` gives undefined behavior if we try to send a response
res . end ( "bar" ) ;
} ) ;
export async function getServerSideProps ( { req , res } ) {
await router . run ( req , res ) ;
return {
props : { } ,
} ;
}handler() โดยตรงใน getServerSideProps // page/index.js
const router = createRouter ( ) . use ( foo ) . use ( bar ) ;
const handler = router . handler ( ) ;
export async function getServerSideProps ( { req , res } ) {
await handler ( req , res ) ; // BAD: You should call router.run(req, res);
return {
props : { } ,
} ;
} โปรดดูการสนับสนุนของฉัน
มิกซ์