Metode routing berbasis janji dan lapisan middleware untuk rute API selanjutnya.js, rute API tepi, middleware, router aplikasi next.js, dan getServerDeprops.
npm install next-connect@nextPeriksa juga folder contoh.
next-connect dapat digunakan dalam rute 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 dapat digunakan di rute API tepi
// 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 dapat digunakan di Next.js 13 Route Handler. Cara penangan ditulis hampir sama dengan rute API EDGE Next.js dengan menggunakan 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 dapat digunakan di Middleware 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 dapat digunakan di 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 ) ;
} API berikut ditulis ulang dalam hal NodeRouter ( createRouter ), tetapi mereka berlaku untuk EdgeRouter ( createEdgeRouter ) juga.
Buat instance node.js router.
base (Opsional) - Cocokkan semua rute di sebelah kanan base atau cocokkan semua jika dihilangkan. (Catatan: Jika digunakan di Next.js, ini sering dihilangkan)
fn (s) bisa:
(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 adalah metode HTTP ( GET , HEAD , POST , PUT , PATCH , DELETE , OPTIONS , TRACE ) dalam huruf kecil.
pattern (Opsional) - Rute kecocokan berdasarkan pola yang didukung atau cocok dengan apapun jika dihilangkan.
fn (s) adalah fungsi dari (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" ) ;
} ) ;Catatan Anda harus memahami routing berbasis sistem file selanjutnya.js. Misalnya, memiliki
router.put("/api/foo", handler)Di dalampage/api/index.jstidak melayani penangan di/api/foo.
Sama seperti .method tetapi menerima metode apa pun .
Buat pawang untuk menangani permintaan yang masuk.
Options.onError
Menerima fungsi sebagai penangan kesalahan semua; dieksekusi setiap kali seorang pawang melakukan kesalahan. Secara default, ia merespons dengan 500 Internal Server Error generik saat mencatat kesalahan ke 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 } ) ;Options.onnoMatch
Menerima fungsi (req, res) sebagai penangan ketika tidak ada rute yang cocok. Secara default, ia merespons dengan status 404 dan Route [Method] [Url] not found tubuh.
function onNoMatch ( req , res ) {
res . status ( 404 ) . end ( "page is not found... or is it!?" ) ;
}
export default router . handler ( { onNoMatch } ) ; Menjalankan req dan res melalui rantai middleware dan mengembalikan janji . Itu diselesaikan dengan nilai yang dikembalikan dari penangan.
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" Jika kesalahan dilemparkan ke dalam rantai, router.run akan menolak. Anda juga dapat menambahkan TRY-Catch di middleware pertama untuk menangkap kesalahan sebelum menolak panggilan .run() :
router
. use ( async ( req , res , next ) => {
return next ( ) . catch ( errorHandler ) ;
} )
. use ( thisMiddlewareMightThrow ) ;
await router . run ( req , res ) ; Ada beberapa jebakan dalam menggunakan next-connect . Di bawah ini adalah hal -hal yang perlu diingat untuk menggunakannya dengan benar.
await next() Jika next() tidak ditunggu, kesalahan tidak akan ditangkap jika mereka dilemparkan ke penangan async, yang mengarah ke 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("?");
} ) ;Masalah lain adalah bahwa pawang akan menyelesaikan sebelum semua kode di setiap lapisan berjalan.
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 yang sama seperti pola di bawah ini: // 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 ( ) ; Ini karena, di setiap rute API, instance router yang sama bermutasi, yang mengarah ke perilaku yang tidak ditentukan. Jika Anda ingin mencapai sesuatu seperti itu, Anda dapat menggunakan router.clone untuk mengembalikan berbagai contoh dengan rute yang sama yang diisi.
// 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 atau res.redirect di dalam 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() langsung di 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 : { } ,
} ;
} Silakan lihat kontribusi saya.
Mit