【发布时间】:2019-08-23 22:12:02
【问题描述】:
这是我的第一篇文章,如果问题需要一些更改,我们深表歉意。我已经尽可能地简化了这个问题,但是这里有很多组件,所以这篇文章非常庞大......
我们的 ASP.NET MVC 站点部署为 Azure 上的应用服务。我正在使用 API 控制器方法来生成存在于同一站点上的页面的 PDF。为此,控制器创建一个 PhantomJS 进程,等待成功,然后返回它创建的文件的内容。这一切都很好,但之后网站上的几个视图会产生如下错误:
“/”应用程序中的服务器错误。
目录 'D:\home\site\wwwroot\Views\Location' 不存在。无法开始监控文件更改。
说明:在执行当前 Web 请求期间发生了未处理的异常。请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。
异常详细信息:System.Web.HttpException:目录“D:\home\site\wwwroot\Views\Location”不存在。无法开始监控文件更改。
一段时间后,错误发生变化:
“/”应用程序中的服务器错误。
未找到视图“LocationList”或其主视图,或者没有视图引擎支持搜索到的位置。搜索了以下位置:
说明:在执行当前 Web 请求期间发生了未处理的异常。请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。
~/Views/Location/LocationList.aspx
~/Views/Location/LocationList.ascx
~/Views/Shared/LocationList.aspx
~/Views/Shared /LocationList.ascx
~/Views/Location/LocationList.cshtml
~/Views/Location/LocationList.vbhtml
~/Views/Shared/LocationList.cshtml
~/Views/Shared/LocationList .vbhtml
异常详细信息:System.InvalidOperationException:未找到视图“LocationList”或其主视图或没有视图引擎支持搜索的位置。搜索了以下位置:
~/Views/Location/LocationList.aspx
~/Views/Location/LocationList.ascx
~/Views/Shared/LocationList.aspx
~/Views/Shared /LocationList.ascx
~/Views/Location/LocationList.cshtml
~/Views/Location/LocationList.vbhtml
~/Views/Shared/LocationList.cshtml
~/Views/Shared/LocationList .vbhtml
这仅适用于尚未编译的视图,或以前未访问过的任何其他文件。修复它的唯一方法是手动停止和启动 Web 应用程序。我可以确认并非所有进程都会发生这种情况(运行“echo.exe”而不是“phantomjs.exe”不会导致损坏的行为)。
我查看了所有我能想到的日志,没有发现任何异常。我最好的猜测是一个进程被强制或意外终止,但至于什么以及为什么,我不知道。也许有一些我不知道的重要日志?
下面是相关的c#代码:
private static async Task<int> ExecuteSimpleAsync(string workingDir, double? timeout,
string command, params string[] parameters)
{
var paramStr = string.Join(" ", parameters.Select(x => x == null ? "" : $"\"{x}\"").ToList());
var processInfo = new ProcessStartInfo(command, paramStr) {
WorkingDirectory = workingDir,
UseShellExecute = false,
CreateNoWindow = true,
};
Process process = null;
int exitCode = -1;
using (process = new Process() { StartInfo = processInfo }) {
process.Start();
await process.WaitForExitAsync(timeout); // simple extension function to check for 'Process.HasExited' periodically
exitCode = process.ExitCode;
}
return exitCode;
}
private static async Task<byte[]> GetFileContents(string filePath) {
byte[] bytes = null;
using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
bytes = new byte[file.Length];
await file.ReadAsync(bytes, 0, (int) file.Length);
}
return bytes;
}
public static async Task<byte[]> RenderPdfAsync(
string cookiesB64, string localUrl, string baseFilename, double? timeout = 60)
{
....
// filesPath: (directory for temporary output)
// timeout: 60.000 (60 seconds)
// PhantomJSExePath: (absolute path containing 'phantomjs.exe')
// scriptFile: "rasterize_simple.js"
// requestUrl: "TestReport/ForUserAndTestPdf/1002/10"
// outputFile: "phantomjs-output-<timestamp>.pdf"
// cookiesB64: (base64-encoded authentication cookies passed to request in PhantomJS)
var exitCode = await ExecuteSimpleAsync(filesPath, timeout, PhantomJSExePath + @"\phantomjs.exe",
scriptFile, requestUrl, outputFile, cookiesB64);
if (exitCode != 0)
return null;
return await GetFileContents(outputFile);
}
[Authorize]
[HttpGet]
[Route("TestReport/ForUserAndTestPdf/{userId}/{testId}")]
public async Task<HttpResponseMessage> ForUserAndTestPdfAsync(int userId, int testId) {
// produce a slightly-modified version of the current URL:
// /TestReport/ForUserAndTest/<userid>/<testid>
// => /TestReport/ForUserAndTestPdf/<userid>/<testid>?print=true
var url = Request.RequestUri.GetLocalPathWithParams("print=true").Replace("ForUserAndTest", "ForUserAndTestPdf");
// get the cookies used in the current request and convert to a base64-encoded JSON object
var cookiesB64 = Request.GetCookiesJsonB64();
var bytes = await PhantomJSHelpers.RenderPdfAsync(cookiesB64, url, "phantomjs-output", 60);
var message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StreamContent(new MemoryStream(bytes));
message.Content.Headers.ContentLength = bytes.Length;
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return message;
}
这是 PhantomJS 使用的“rasterize_simple.js”脚本的相关部分,没有设置页面大小、cookie 等:
page.open(address, function(status) {
page.render(outputFilename);
phantom.exit(0);
});
所有这一切的预期结果是它生成的 PDF 文件,并且对该 API 方法的所有后续调用(使用不同的参数)都可以正常工作。然而,副作用是一个完全损坏的网站:(
这里的任何帮助将不胜感激!
【问题讨论】:
标签: azure asp.net-mvc-4 asp.net-web-api phantomjs azure-web-app-service