توجيه الأسلوب القائم على الوعد وطبقة الوسيطة لطرق API Next.js ، وطرق API Edge ، و Middleware ، و Next.js App Router ، و 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 Edge
// 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. طريقة مكتوبة معالجات هي نفسها تقريبًا إلى مسارات API Next.js Edge باستخدام 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 ) ;
} تتم إعادة كتابة واجهات برمجة التطبيقات التالية في مصطلح NodeRouter ( createRouter ) ، لكنها تنطبق على EdgeRouter ( createEdgeRouter ) أيضًا.
إنشاء مثيل Node.js التوجيه.
base (اختيارية) - تطابق جميع الطرق على يمين base أو مطابقة كل شيء إذا تم حذفه. (ملاحظة: إذا تم استخدامها في Next.js ، غالبًا ما يتم حذف هذا)
يمكن أن يكون fn (s):
(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 ) في Secondcase.
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.
/api/fooسبيلpage/api/index.js، لا يخدم وجود جهازrouter.put("/api/foo", handler).
مثل .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 : { } ,
} ;
} يرجى الاطلاع على مساهمي.
معهد ماساتشوستس للتكنولوجيا