Nextjs 构建应用基础: 路由和中间件一、路由(以App Router为例): 1.Nextjs使用项目目录结构去定
一、路由(以App Router为例):
1.Nextjs使用项目目录结构去定义路由:
在对应路由文件夹下:
page.js
文件定义页面layout.js
处理布局(page的内容会作为layout的children传入)template.js
(模板的路由之间导航时,会重新挂载children的新实例,创建 DOM 元素,不hi像布局一样维持状态)
其他特殊页面:
loading
展示loading、 error
展示错误等
2.重定向API
参考 Routing: Redirecting | Next.js (nextjs.org)
3.路由组
可以将项目文件按照一定逻辑去分组,同时避免影响页面路径:
4.平行路由
平行路由功能以插槽方式使用
其有如下使用方式:
- 在layout内部有条件的显示页面
- 允许为每个路由定义独立错误和加载页面
- 插槽内添加子页面方便创建标签页组
import Link from 'next/link'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<nav>
<Link href="/page-views">Page Views</Link>
<Link href="/visitors">Visitors</Link>
</nav>
<div>{children}</div>
</>
)
}
- 利于处理模态框时遇到的问题(深度链接,同时在页面刷新和导航时保持模态窗口的状态。) 页面具体结构参考文档Routing: Parallel Routes | Next.js (nextjs.org)
5.拦截路由
拦截路由时需要处理文件夹命名:
(.)
匹配同一层级(..)
匹配上一层级(..)(..)
匹配上上层级。(...)
匹配根目录app
二、路由处理程序(以路由方式处理请求和响应):
路由处理程序需要名为router
的文件,并且要在app
目录下, 但是不能和page
文件同目录:
支持这些HTTP方法:GET
, POST
, PUT
, PATCH
, DELETE
, HEAD
, OPTIONS
1.路由处理程序的一些行为:
- 使用GET缓存: 在路由处理程序顶部添加
export const dynamic = 'force-static'
- 特殊的保持静态的路由处理程序:路由: 路由处理器 |Next.js (nextjs.org)
- 注意路由处理程序和页面冲突的情况
2.使用例子:
- 可以使用增量静态重新生成 (ISR): 重新验证缓存的数据
- Cookie:
- 获取请求Cookie:
// app/api/route.js export async function GET(request) { const token = request.cookies.get('token') request.cookies.set(`token2`, 123) }
- 设置响应Cookie:
// app/api/route.js import { cookies } from 'next/headers' export async function GET(request) { const cookieStore = cookies() const token = cookieStore.get('token') return new Response('Hello, Next.js!', { status: 200, headers: { 'Set-Cookie': `token=${token}` }, }) }
- Header
- 使用next/headers
import { headers } from 'next/headers' export async function GET(request: Request) { const headersList = headers() const referer = headersList.get('referer') return new Response('Hello, Next.js!', { status: 200, headers: { referer: referer }, }) }
// app/api/route.js export async function GET(request) { const headersList = new Headers(request.headers) const referer = headersList.get('referer') }
- 重定向
import { redirect } from 'next/navigation' export async function GET(request: Request) { redirect('https://nextjs.org/') }
- 获取动态路由值
export async function GET(
request: Request,
{ params }: { params: { slug: string } }
) {
const slug = params.slug // 'a', 'b', or 'c'
}
- 获取URL请求参数
import { type NextRequest } from 'next/server'
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('query')
// query is "hello" for /api/search?query=hello
}
- 支持流式处理 流式处理
- 获取请求体
// 普通请求体 export async function POST(request: Request) { const res = await request.json() return Response.json({ res }) // formData export async function POST(request: Request) { const formData = await request.formData() const name = formData.get('name') const email = formData.get('email') return Response.json({ name, email })
- 设置CORS
export async function GET(request: Request) { return new Response('Hello, Next.js!', { status: 200, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }, })
- 非UI内容返回
export async function GET() { return new Response( `<?xml version="1.0" encoding="UTF-8" ?> <rss version="2.0"> <channel> <title>Next.js Documentation</title> <link>https://nextjs.org/docs</link> <description>The React Framework for the Web</description> </channel> </rss>`, { headers: { 'Content-Type': 'text/xml', }, } ) }
三、中间件
使用中间件可以拦截和处理应用中的请求和响应:
其应用场景包括:身份验证和授权
、服务器端重定向
、路径重写
,爬虫程序检测
,日志记录和分析
,功能标记
等;
1.定义中间件
在项目的根目录定义 middleware
文件,例子如下:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
// See "Matching Paths" below to learn more
export const config = {
matcher: '/about/:path*',
}
上面将/about/
路径重定向到home
路径下
3.注意路由响应过程中对应配置的执行顺序
- headers(
next.config.js
) - redirects(
next.config.js
) - 中间件 (
rewrites
,redirects
等) - beforeFiles(
next.config.js
中rewrites
) - 文件系统的路由 (
public/
,_next/static/
,pages/
,app/
) - afterFiles (
next.config.js
中rewrites
) - 动态路由 (
/blog/[slug]
) - fallback (
next.config.js
中rewrites
)
2.路径匹配器
- 匹配单一路径或多个路径
export const config = {
matcher: '/about/:path*',
matcher: ['/about/:path*', '/dashboard/:path*'],
}
- 使用条件语句
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}
3.可以执行的操作
- 使用NextResponse处理响应
- 对Cookie的读取和处理:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Assume a "Cookie:nextjs=fast" header to be present on the incoming request
// Getting cookies from the request using the `RequestCookies` API
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// Setting cookies on the response using the `ResponseCookies` API
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// The outgoing response will have a `Set-Cookie:vercel=fast;path=/` header.
return response
}
- 设置请求和响应头
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Clone the request headers and set a new header `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// You can also set request headers in NextResponse.next
const response = NextResponse.next({
request: {
// New request headers
headers: requestHeaders,
},
})
// Set a new response header `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
- 设置CORS
import { NextRequest, NextResponse } from 'next/server'
const allowedOrigins = ['https://acme.com', 'https://my-app.org']
const corsOptions = {
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
export function middleware(request: NextRequest) {
// Check the origin from the request
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)
// Handle preflighted requests
const isPreflight = request.method === 'OPTIONS'
if (isPreflight) {
const preflightHeaders = {
...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
...corsOptions,
}
return NextResponse.json({}, { headers: preflightHeaders })
}
// Handle simple requests
const response = NextResponse.next()
if (isAllowedOrigin) {
response.headers.set('Access-Control-Allow-Origin', origin)
}
Object.entries(corsOptions).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
export const config = {
matcher: '/api/:path*',
}
- 直接生成一个响应
import type { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Limit the middleware to paths starting with `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request: NextRequest) {
// Call our authentication function to check the request
if (!isAuthenticated(request)) {
// Respond with JSON indicating an error message
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}
- 延长中间件的生命周期 类似
await
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'
export function middleware(req: NextRequest, event: NextFetchEvent) {
event.waitUntil(
fetch('https://my-analytics-platform.com', {
method: 'POST',
body: JSON.stringify({ pathname: req.nextUrl.pathname }),
})
)
return NextResponse.next()
}
- 高级中间件标志参考文档
总结
在Next.js中,路由和中间件是构建应用的关键概念。通过App Directory,Next.js提供了灵活的路由系统,支持定义页面、布局和模板。平行路由允许开发者在同一个布局中同时或条件性地渲染多个页面,非常适合动态应用场景,如仪表板。路由处理程序则允许开发者在app
目录下创建自定义的请求处理逻辑,支持多种HTTP方法,并能利用Next.js提供的扩展API。
中间件则为应用提供了在请求到达页面或API之前进行拦截和处理的能力,适用于身份验证、重定向、CORS设置等多种场景。开发者可以通过定义middleware.js
文件来创建中间件,并利用Next.js提供的API进行复杂的请求处理。
转载自:https://juejin.cn/post/7413682017298858018