Node.js搭建HTTP服务页面链接显示但无法正常点击跳转
问题原因
- 核心原因:你生成的a标签用了
file:///开头的本地文件协议链接。现代浏览器有强制安全策略,禁止从HTTP/HTTPS加载的页面直接跳转/加载本地file协议资源,防止恶意网站随意读取用户本地文件。你手动复制链接粘贴到地址栏能打开,是因为这个操作是用户主动触发的,不受该安全策略限制。 - 附带的代码bug:
- 表格闭合标签拼写错误:开标签是
<table>,结束标签错写成</tables>,会导致HTML结构解析异常 - 日志路径前后不一致:读取日志列表用的路径是
G:/logs/,拼接链接时写的是G:/NodeOctaveService/logs/,路径不匹配会导致后续找不到文件 fs.readdirSync是同步API,不需要传入回调函数,传入的回调不会生效,出错时会直接抛出异常- 现有服务仅处理了根路径的HTML返回,没有配置日志文件的请求路由,就算把链接改成相对路径,点击后也会返回404
- h1标签错误写在了head标签内,不符合HTML规范
- 响应头未指定字符集,容易出现中文乱码
- 表格闭合标签拼写错误:开标签是
修复方案
把链接改成服务端相对路径,同时给服务端加日志文件的路由处理逻辑,让服务端动态返回请求的日志内容即可,修复后的完整代码如下:
/* ------------------------------------------------ ------------------------------------------------ <<<<<<<<<<<<< Below created by J.E >>>>>>>>>>>>> ------------------------------------------------ ------------------------------------------------ */ /* requires */ var fs = require('fs') const http = require("http"); const path = require('path'); // 引入path处理路径拼接,规避跨平台路径问题 /*---------------------- setting up the server ----------------------*/ const host = 'localhost'; const port = 8080; // 统一日志目录配置,避免前后路径不一致 const logdirname = "G:/logs/"; // 服务启动时读取一次日志列表即可,无需每次请求重复读取 let all_logs = []; try { all_logs = fs.readdirSync(logdirname); } catch (err) { console.error('读取日志目录失败:', err); } let link = "<table>"; all_logs.forEach((log_, i) => { // 替换file协议链接为服务端相对路径,对文件名做编码处理特殊字符 link += `<tr><td><a href="/logs/${encodeURIComponent(log_)}">Log File ${i}</a></td></tr>`; }) // 修正表格闭合标签拼写错误 link += "</table>"; http.createServer(function (req, res) { // 路由判断:匹配日志文件请求 if (req.url.startsWith('/logs/')) { // 提取请求的日志文件名,做安全校验防止路径穿越攻击 const logFileName = decodeURIComponent(req.url.replace('/logs/', '')); const logFilePath = path.join(logdirname, logFileName); // 拦截恶意构造的跨目录路径请求,避免服务器其他文件被非法读取 if (!logFilePath.startsWith(path.resolve(logdirname))) { res.writeHead(403); res.end('Forbidden'); return; } // 读取目标日志文件返回 fs.readFile(logFilePath, (err, data) => { if (err) { res.writeHead(404); res.end('Log file not found'); return; } res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' // 指定纯文本类型和编码,避免乱码 }); res.end(data); }) return; } // 根路径返回日志列表首页 var html = buildHtml(); res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' // 加字符集配置避免页面中文乱码 }); res.end(html); }).listen(port); console.log(`Server is running on http://${host}:${port}`); /*------------------------ 生成首页HTML ------------------------*/ function buildHtml(){ var header = ` <title>Octave Service Links</title> <style> *, html{ margin: 0; padding: 0; border: 10; border-color: blueviolet; } html{ width: 100%; height: 100%; } body{ width: 100%; height: 100%; position: relative; background-color:lightgoldenrodyellow; } .center{ width: 100%; height: 50%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color:darkgreen; font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; text-align: center; } h1{ font-size: 80px; color: blueviolet; } </style> `; // 将h1移到body内,符合HTML规范 var body = ` <h1> Click the link to the log file you want to open. </h1> ${link} `; return "<!DOCTYPE html>" + "<html><head>" + header + "</head><body>" + body + "</body></html>"; }
内容的提问来源于stack exchange,提问作者James_Calvert
相关产品推荐
相关产品推荐

