Indy Http Server 交付JavaScript出现语法错误的技术问询
我尝试用Indy部署Swagger UI(通过提供JavaScript文件渲染API文档),实现代码如下:
procedure TfmMain.SendJavaScriptFileResponse(AResponseInfo: TIdHTTPResponseInfo; AFileName: String); begin AResponseInfo.ContentType := 'application/javascript'; AResponseInfo.CharSet := 'utf-8'; var LFileContents := TStringList.Create; try LFileContents.LoadFromFile(AFileName); AResponseInfo.ContentText := LFileContents.Text; finally LFileContents.Free; end; end;
浏览器运行该JavaScript时抛出语法错误:
Uncaught SyntaxError: illegal character U+20AC
从Indy IdHttpServer收到的响应头如下:
HTTP/1.1 200 OK Connection: close Content-Encoding: utf-8 Content-Type: application/javascript; charset=utf-8 Content-Length: 1063786 Date: Sun, 05 Feb 2023 20:45:56 GMT
但相同的JavaScript文件放到托管网站提供时,浏览器运行完全正常。想问用Indy HTTP Server发送JS文件时,是否需要调整特定设置或字符集?
问题根源
你的代码用TStringList.LoadFromFile读取JS文件时,默认会使用系统默认ANSI编码解析文件,而非UTF-8。如果JS文件本身是UTF-8编码(尤其是包含€(U+20AC)这类非ANSI字符时),ANSI编码读取会导致字符乱码,最终传给浏览器的内容就会包含非法字符。
另外响应头里多余的Content-Encoding: utf-8是错误设置——这个头用于标识内容的压缩编码(比如gzip),不是字符编码,会让浏览器误解内容处理逻辑。
修复方案
正确读取UTF-8文件
改用TStringList的重载方法,明确指定UTF-8编码读取文件:procedure TfmMain.SendJavaScriptFileResponse(AResponseInfo: TIdHTTPResponseInfo; AFileName: String); begin AResponseInfo.ContentType := 'application/javascript'; AResponseInfo.CharSet := 'utf-8'; // 移除错误的Content-Encoding设置 AResponseInfo.ContentEncoding := ''; var LFileContents := TStringList.Create; try // 指定UTF-8编码读取文件 LFileContents.LoadFromFile(AFileName, TEncoding.UTF8); AResponseInfo.ContentText := LFileContents.Text; finally LFileContents.Free; end; end;更高效的文件发送方式
如果JS文件体积较大,直接用ContentStream发送原始字节流,避免编码转换带来的问题,同时提升性能:procedure TfmMain.SendJavaScriptFileResponse(AResponseInfo: TIdHTTPResponseInfo; AFileName: String); begin AResponseInfo.ContentType := 'application/javascript'; AResponseInfo.CharSet := 'utf-8'; AResponseInfo.ContentEncoding := ''; // 直接发送文件流,跳过字符串转换步骤 AResponseInfo.ContentStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); // Indy会自动计算并设置Content-Length,也可手动指定 // AResponseInfo.ContentLength := AResponseInfo.ContentStream.Size; end;注意:使用
ContentStream时,Indy会在请求结束后自动释放流,无需手动调用Free。验证文件编码
确认你的JS文件确实是UTF-8编码(可通过记事本打开后选择“另存为”查看编码选项)。无论文件带不带BOM,TEncoding.UTF8都能正确读取。
内容的提问来源于stack exchange,提问作者Tav

