Select.HtmlToPdf IIS部署后不保存PDF仅显示源页面问题排查
Select.HtmlToPdf IIS部署后流程中断、直接展示源页面问题排查方案
问题表现
- 预期逻辑:后台静默拉取指定HTML页面生成PDF,完成文件保存、邮件发送、数据库更新后跳转至列表页,全程不向用户暴露待转换的源HTML页面
- Visual Studio开发环境运行完全符合预期
- IIS生产环境异常:直接向用户返回FactuurPDF源页面,后续PDF保存、邮件发送、数据库更新、页面跳转逻辑全部中止
核心实现代码如下:
// other stuff HtmlToPdf converter = new HtmlToPdf(); var TxtUrl = ""; TxtUrl = "https://localhost:44368/Facturen/FactuurPDF" + "?id=" + id + "&fact=" + fact; PdfDocument doc = converter.ConvertUrl(TxtUrl); var dat = DateTime.Today.ToString("yyyy-MM-dd"); doc.Save(@Path.Combine(_env.WebRootPath, "Verzondenfacturen/Fact_" + id + "_" + dat + ".pdf")); doc.Close(); //other stuff (email pdf and update database) return LocalRedirect("/Facturen/Index");
排查思路
按优先级从高到低排查以下问题点:
- 硬编码转换地址错误:代码中写死的
https://localhost:44368是VS启动IIS Express分配的本地调试端口,生产环境IIS不会监听这个端口。Select.HtmlToPdf内部通过HTTP请求拉取待转换页面,请求不存在的地址会触发请求异常,若全局异常配置不当会直接返回路由匹配到的FactuurPDF页面,中断后续流程。 - 应用程序池权限不足:Select.HtmlToPdf依赖内置WebKit内核渲染页面,需要启动独立子进程,默认IIS应用程序池使用的ApplicationPoolIdentity身份权限极低,既没有启动渲染进程的权限,也没有写入
Verzondenfacturen目录的权限,会在转换或保存步骤抛出未捕获异常。 - 平台目标不匹配:如果项目编译配置勾选了「32位优先」,但IIS应用程序池未开启「启用32位应用程序」,或者反过来,会导致组件加载失败直接抛出异常。
- 源页面鉴权拦截:如果FactuurPDF页面需要登录认证才能访问,Select.HtmlToPdf默认发起的请求不带当前用户的认证Cookie,生产环境若开启了身份认证,组件拉取到的是登录页或302跳转响应,部分版本的组件遇到这类非200响应会直接把响应内容回传给客户端,表现为直接展示源页面/登录页。
- 缺失异常捕获:现有代码没有任何异常处理逻辑,转换、保存任意步骤抛出异常时,若全局异常过滤配置了自动返回当前路由关联的页面结果,就会出现直接展示源页的现象。
可行修复方案
- 替换硬编码地址:通过当前HTTP请求上下文动态拼接站点可访问地址,不要写死调试用的localhost端口。
- 配置IIS运行环境:
- 将对应站点的应用程序池托管管道模式设为集成模式
- 应用程序池高级设置中把「加载用户配置文件」设为
True - 匹配平台位数:和项目编译目标一致,设置应用程序池「启用32位应用程序」为对应状态
- 给站点下的
Verzondenfacturen目录添加IIS_IUSRS用户组的读写、修改权限
- 补充鉴权凭据传递:如果源页面需要登录访问,手动将当前请求的Cookie头传给转换器,避免被鉴权拦截。
- 补充异常捕获和目录检查逻辑:转换前先确保保存目录存在,捕获转换流程的异常并记录日志,方便定位生产环境具体报错。
修正后参考代码
// other stuff try { HtmlToPdf converter = new HtmlToPdf(); // 动态拼接当前站点可访问地址,替换硬编码的调试地址 var currentRequest = HttpContext.Request; var siteBase = $"{currentRequest.Scheme}://{currentRequest.Host}{currentRequest.PathBase}"; var convertUrl = $"{siteBase}/Facturen/FactuurPDF?id={id}&fact={fact}"; // 源页面需要鉴权时,传递当前用户的认证Cookie converter.Options.HttpCookies = currentRequest.Headers["Cookie"].ToString(); PdfDocument doc = converter.ConvertUrl(convertUrl); var dateStr = DateTime.Today.ToString("yyyy-MM-dd"); var saveDir = Path.Combine(_env.WebRootPath, "Verzondenfacturen"); // 自动创建不存在的保存目录 Directory.CreateDirectory(saveDir); var savePath = Path.Combine(saveDir, $"Fact_{id}_{dateStr}.pdf"); doc.Save(savePath); doc.Close(); //other stuff (email pdf and update database) } catch (Exception ex) { // 此处添加异常日志记录逻辑,生产环境可精准定位报错点 throw; } return LocalRedirect("/Facturen/Index");
内容的提问来源于stack exchange,提问作者leof
相关产品推荐
相关产品推荐

