如题,项目前端是nuxt2做的,现在遇到如下问题
当通过非正常编码编码后的url 访问的时候, pm2日志中报错 "URI malformed"
探究了一下,错误是因为 /node_moduels/@nuxt/server/dist/server.js 文件中的
const nuxtMiddleware = ({ options, nuxt, renderRoute, resources }) => async function nuxtMiddleware (req, res, next) {
// Get context
const context = utils.getContext(req, res);
try {
const url = decodeURI(req.url); //这里
res.statusCode = 200;
const result = await renderRoute(url, context);
// If result is falsy, call renderLoading
if (!result) {
await nuxt.callHook('server:nuxt:renderLoading', req, res);
return
}
await nuxt.callHook('render:route', url, result, context);
const {
html,
cspScriptSrcHashes,
error,
redirected,
preloadFiles
} = result;
if (redirected && context.target !== utils.TARGETS.static) {
await nuxt.callHook('render:routeDone', url, result, context);
return html
}
if (error) {
res.statusCode = context.nuxt.error.statusCode || 500;
}
if (options.render.csp && cspScriptSrcHashes) {
const { allowedSources, policies } = options.render.csp;
const isReportOnly = !!options.render.csp.reportOnly;
const cspHeader = isReportOnly ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy';
res.setHeader(cspHeader, getCspString({ cspScriptSrcHashes, allowedSources, policies, isReportOnly }));
}
// Add ETag header
if (!error && options.render.etag) {
const { hash } = options.render.etag;
const etag = hash ? hash(html, options.render.etag) : generateETag__default['default'](html, options.render.etag);
if (fresh__default['default'](req.headers, { etag })) {
res.statusCode = 304;
await nuxt.callHook('render:beforeResponse', url, result, context);
res.end();
await nuxt.callHook('render:routeDone', url, result, context);
return
}
res.setHeader('ETag', etag);
}
// HTTP2 push headers for preload assets
if (!error && options.render.http2.push) {
// Parse resourceHints to extract HTTP.2 prefetch/push headers
// https://w3c.github.io/preload/#server-push-http-2
const { shouldPush, pushAssets } = options.render.http2;
const { publicPath } = resources.clientManifest;
const links = pushAssets
? pushAssets(req, res, publicPath, preloadFiles)
: defaultPushAssets(preloadFiles, shouldPush, publicPath, options);
// Pass with single Link header
// https://blog.cloudflare.com/http-2-server-push-with-multiple-assets-per-link-header
// https://www.w3.org/Protocols/9707-link-header.html
if (links.length > 0) {
res.setHeader('Link', links.join(', '));
}
}
// Send response
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.setHeader('Accept-Ranges', 'none'); // #3870
res.setHeader('Content-Length', Buffer.byteLength(html));
await nuxt.callHook('render:beforeResponse', url, result, context);
res.end(html, 'utf8');
await nuxt.callHook('render:routeDone', url, result, context);
return html
} catch (err) {
if (context && context.redirected) {
consola__default['default'].error(err);
return err
}
if (err.name === 'URIError') { //这里
err.statusCode = 400;
}
next(err);
}
};
其中
const url = decodeURI(req.url);
对url无法进行解析,导致的.
需求是想在遇到"URI malformed"错误的时候,直接跳首页,目前采用了两种做法解决
在nuxt.config.js文件中,增加 serverMiddleware 配置项
serverMiddleware: [
(req, res, next) => {
// 自定义中间件逻辑...
try {
//尝试解析url
decodeURIComponent(req.url)
//往下继续执行
next();
} catch {
//解析错误就跳转
res.writeHead(302, {
Location: '/'
});
res.end();
}
}
],
这样可以解决,但是无法阻止server.js 文件会对url再次进行解析,这样比较消耗资源
直接修改 /node_moduels/@nuxt/server/dist/server.js 文件
在
if (err.name === 'URIError') {
err.statusCode = 400;
}
中加入:
if (err.name === 'URIError') {
err.statusCode = 400;
//新加
res.writeHead(302, { Location: '/' })
//新加
return res.end()
}
这样也能起到效果,但是这样就直接修改了核心代码
有没有更好的办法处理?求解
注意: 这个是服务端的错误,无法通过定义路由中间件的形式捕获(即在/middleware 目录中定义中间件,在serverMiddleware中使用,这样加载不出来,已试)
引用 皆我百晓生 小程序回复内容作答:
可以通过自定义错误处理中间件来捕获服务端错误,然后重定向到首页。在nuxt.config.js文件中的serverMiddleware配置项中添加自定义错误处理中间件,代码如下:
import consola from 'consola';
export default function (req, res, next) {
try {
decodeURIComponent(req.url)
next();
} catch (error) {
// 处理URI malformed错误
if (error instanceof URIError) {
res.writeHead(302, {
Location: '/'
});
return res.end();
}
// 处理其他服务端错误
consola.error(error);
res.writeHead(500, {
'Content-Type': 'text/html'
});
return res.end('Internal Server Error');
}
}
然后在nuxt.config.js中将自定义错误处理中间件添加到serverMiddleware中:
serverMiddleware: [
'~/middleware/error-handler.js'
]
这样就能捕获并处理服务端错误了。同时,通过将错误处理中间件放在serverMiddleware中的顶部,可以确保该中间件第一个被执行,从而避免了其他中间件对URL进行解析的问题。
【以下回答由 GPT 生成】
Nuxt.js的nuxtMiddleware
函数中的错误捕获部分可以通过添加try...catch
语句来实现。下面是修改后的代码示例:
const nuxtMiddleware = ({ options, nuxt, renderRoute, resources }) => async function nuxtMiddleware (req, res, next) {
// Get context
const context = utils.getContext(req, res);
try {
const url = decodeURI(req.url); // 可能引发错误的代码
res.statusCode = 200;
const result = await renderRoute(url, context);
// If result is falsy, call renderLoading
if (!result) {
await nuxt.callHook('server:nuxt:renderLoading', req, res);
return
}
await nuxt.callHook('render:route', url, result, context);
const {
html,
cspScriptSrcHashes,
error,
redirected,
preloadFiles
} = result;
if (redirected && context.target !== utils.TARGETS.static) {
await nuxt.callHook('render:routeDone', url, result, context);
return html
}
if (error) {
res.statusCode = context.nuxt.error.statusCode || 500;
}
if (options.render.csp && cspScriptSrcHashes) {
const { allowedSources, policies } = options.render.csp;
const isReportOnly = !!options.render.csp.reportOnly;
const cspHeader = isReportOnly ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy';
res.setHeader(cspHeader, getCspString({ cspScriptSrcHashes, allowedSources, policies, isReportOnly }));
}
// Add ETag header
if (!error && options.render.etag) {
const { hash } = options.render.etag;
const etag = hash ? utils.etag(html, hash) : utils.etag(html);
if (req.headers['if-none-match'] === etag) {
res.statusCode = 304
} else {
res.setHeader('ETag', etag);
}
}
//...
} catch (error) {
// handle error
console.error('服务端错误:', error); // 打印错误信息到控制台
// 写入错误状态码到响应头
res.statusCode = 500; // 可根据具体需求修改状态码
// 发送错误响应给客户端
res.end('服务器出现了错误,请稍后重试');
}
}
修改后的代码将错误捕获部分放在了一个try...catch
语句块中,在catch块中打印错误到控制台,并发送一个适当的错误响应给客户端。你可以根据具体需求修改错误状态码和错误响应内容。
请注意,在实际项目中,你可能还需要根据错误类型进行不同的错误处理,这个代码示例只是一个简单的处理方式。
请注意将上述代码添加到适当的位置,并将其与nuxtMiddleware
函数替换掉原始的代码段。