Next.js API路由,Edge API路由,中間件,Next.js App Router和GetServerVersIdeProps的基於Promor的方法路由和中間件層。
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路由處理程序中使用。使用createEdgeRouter編寫處理程序的方式與Next幾乎相同。 JSEdge API路由。
// 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 ) ;
} 以下API在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 ,POST, PUT ,PAST,PAST,PAST,PAST, PATCH ,PAST, 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基於文件系統的路由。例如,在
page/api/index.js內使用router.put("/api/foo", handler)/api/foo
與。方法相同,但接受任何方法。
創建一個處理程序來處理傳入請求。
選項
接受函數作為全部錯誤處理程序;每當處理程序引發錯誤時執行。默認情況下,它在將錯誤記錄到console時會使用通用500 Internal Server Error響應。
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
當沒有匹配路由時,接受(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將拒絕。您還可以在第一個中間件中添加一個try-catch,以在拒絕.run()呼叫之前捕獲錯誤:
router
. use ( async ( req , res , next ) => {
return next ( ) . catch ( errorHandler ) ;
} )
. use ( thisMiddlewareMightThrow ) ;
await router . run ( req , res ) ; 使用next-connect有一些陷阱。以下是要牢記正確使用它的事情。
await next()如果未等待next() ,則如果將錯誤扔進異步處理程序,則不會捕獲錯誤,從而導致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 : { } ,
} ;
}getServerSideProps中直接使用handler() 。 // 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 : { } ,
} ;
} 請參閱我的貢獻。
麻省理工學院