Adding contract interfaces for specifying runtime compilation exceptions.

Fixes #12
This commit is contained in:
Pranav K 2014-12-19 15:48:37 -08:00
parent 44dadb1d0f
commit 576731e86e
12 changed files with 822 additions and 61 deletions

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Diagnostics
{
/// <summary>
/// Specifies the contract for an exception representing compilation failure.
/// </summary>
[AssemblyNeutral]
public interface ICompilationException
{
/// <summary>
/// Gets a sequence of <see cref="ICompilationFailure"/> with compilation failures.
/// </summary>
IEnumerable<ICompilationFailure> CompilationFailures { get; }
}
}

View File

@ -0,0 +1,40 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Diagnostics
{
/// <summary>
/// Specifies the contract for a file that fails compilation.
/// </summary>
[AssemblyNeutral]
public interface ICompilationFailure
{
/// <summary>
/// Path of the file that produced the compilation exception.
/// </summary>
string SourceFilePath { get; }
/// <summary>
/// Contents of the file.
/// </summary>
string SourceFileContent { get; }
/// <summary>
/// Contents being compiled.
/// </summary>
/// <remarks>
/// For templated files, the <see cref="SourceFileContent"/> represents the original content and
/// <see cref="CompiledContent"/> represents the transformed content. This property can be null if
/// the exception is encountered during transformation.
/// </remarks>
string CompiledContent { get; }
/// <summary>
/// Gets a sequence of <see cref="ICompilationMessage"/> produced as a result of compilation.
/// </summary>
IEnumerable<ICompilationMessage> Messages { get; }
}
}

View File

@ -0,0 +1,40 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Diagnostics
{
/// <summary>
/// Specifies the contract for diagnostic messages produced as result of compiling an instance
/// of <see cref="ICompilationFailure"/>.
/// </summary>
[AssemblyNeutral]
public interface ICompilationMessage
{
/// <summary>
/// Gets the error message.
/// </summary>
string Message { get; }
/// <summary>
/// Gets the zero-based line index for the start of the compilation error.
/// </summary>
int StartLine { get; }
/// <summary>
/// Gets the zero-based column index for the start of the compilation error.
/// </summary>
int StartColumn { get; }
/// <summary>
/// Gets the zero-based line index for the end of the compilation error.
/// </summary>
int EndLine { get; }
/// <summary>
/// Gets the zero-based column index for the end of the compilation error.
/// </summary>
int EndColumn { get; }
}
}

View File

@ -76,7 +76,70 @@ namespace Microsoft.AspNet.Diagnostics
}
// Assumes the response headers have not been sent. If they have, still attempt to write to the body.
private async Task DisplayException(HttpContext context, Exception ex)
private Task DisplayException(HttpContext context, Exception ex)
{
var compilationException = ex as ICompilationException;
if (compilationException != null)
{
return DisplayCompilationException(context, ex, compilationException);
}
return DisplayRuntimeException(context, ex);
}
private Task DisplayCompilationException(HttpContext context,
Exception ex,
ICompilationException compilationException)
{
var stackFrames = new List<StackFrame>();
var model = new CompilationErrorPageModel()
{
Options = _options,
ErrorDetails = new ErrorDetails
{
Error = ex,
StackFrames = stackFrames
}
};
// For view compilation, the most common case is to stop at the first failing file compiled as part of
// rendering a view. Consequently we'll limit ourselves to displaying errors from the first failure.
var failedCompilationFile = compilationException.CompilationFailures.FirstOrDefault();
if (failedCompilationFile != null)
{
var fileContent = failedCompilationFile.SourceFileContent
.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
foreach (var item in failedCompilationFile.Messages)
{
// Convert 0-based line indexes to 1-based index that the StackFrame expects
var lineIndex = item.StartLine + 1;
var frame = new StackFrame
{
File = failedCompilationFile.SourceFilePath,
Line = lineIndex,
Function = string.Empty
};
if (_options.ShowSourceCode)
{
ReadFrameContent(frame, fileContent, lineIndex, item.EndLine + 1);
frame.ErrorDetails = item.Message;
}
stackFrames.Add(frame);
}
}
var errorPage = new CompilationErrorPage
{
Model = model
};
return errorPage.ExecuteAsync(context);
}
private Task DisplayRuntimeException(HttpContext context, Exception ex)
{
var request = context.Request;
@ -107,7 +170,7 @@ namespace Microsoft.AspNet.Diagnostics
}*/
var errorPage = new ErrorPage(model);
await errorPage.ExecuteAsync(context);
return errorPage.ExecuteAsync(context);
}
private IEnumerable<ErrorDetails> GetErrorDetails(Exception ex, bool showSource)
@ -160,14 +223,22 @@ namespace Microsoft.AspNet.Diagnostics
if (showSource && File.Exists(file))
{
IEnumerable<string> code = File.ReadLines(file);
frame.PreContextLine = Math.Max(lineNumber - _options.SourceCodeLineCount, 1);
frame.PreContextCode = code.Skip(frame.PreContextLine - 1).Take(lineNumber - frame.PreContextLine).ToArray();
frame.ContextCode = code.Skip(lineNumber - 1).FirstOrDefault();
frame.PostContextCode = code.Skip(lineNumber).Take(_options.SourceCodeLineCount).ToArray();
ReadFrameContent(frame, code, lineNumber, lineNumber);
}
return frame;
}
private void ReadFrameContent(StackFrame frame,
IEnumerable<string> code,
int startLineNumber,
int endLineNumber)
{
frame.PreContextLine = Math.Max(startLineNumber - _options.SourceCodeLineCount, 1);
frame.PreContextCode = code.Skip(frame.PreContextLine - 1).Take(startLineNumber - frame.PreContextLine).ToArray();
frame.ContextCode = code.Skip(startLineNumber - 1).Take(1 + Math.Max(0, endLineNumber - startLineNumber));
frame.PostContextCode = code.Skip(startLineNumber).Take(_options.SourceCodeLineCount).ToArray();
}
internal class Chunk
{
public string Text { get; set; }

View File

@ -379,7 +379,7 @@ namespace Microsoft.AspNet.Diagnostics
}
/// <summary>
/// Name
/// Version
/// </summary>
internal static string RuntimeInfoPage_PackageVersionColumnName
{
@ -387,7 +387,7 @@ namespace Microsoft.AspNet.Diagnostics
}
/// <summary>
/// Name
/// Version
/// </summary>
internal static string FormatRuntimeInfoPage_PackageVersionColumnName()
{
@ -570,6 +570,22 @@ namespace Microsoft.AspNet.Diagnostics
return GetString("WelcomeTitle");
}
/// <summary>
/// An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.
/// </summary>
internal static string ErrorPageHtml_CompilationException
{
get { return GetString("ErrorPageHtml_CompilationException"); }
}
/// <summary>
/// An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.
/// </summary>
internal static string FormatErrorPageHtml_CompilationException()
{
return GetString("ErrorPageHtml_CompilationException");
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -230,4 +230,7 @@
<data name="WelcomeTitle" xml:space="preserve">
<value>Your ASP.NET vNext application has been successfully started.</value>
</data>
<data name="ErrorPageHtml_CompilationException" xml:space="preserve">
<value>An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.</value>
</data>
</root>

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Diagnostics.Views
{
/// <summary>
/// Holds data to be displayed on the compilation error page.
/// </summary>
public class CompilationErrorPageModel
{
/// <summary>
/// Options for what output to display.
/// </summary>
public ErrorPageOptions Options { get; set; }
/// <summary>
/// Detailed information about each parse or compilation error.
/// </summary>
public ErrorDetails ErrorDetails { get; set; }
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,101 @@
@using System
@using System.Globalization
@using System.Linq
@using System.Net
@using Views
@functions
{
public CompilationErrorPageModel Model { get; set; }
}
@{
var errorDetail = Model.ErrorDetails;
Response.StatusCode = 500;
Response.ContentType = "text/html";
Response.ContentLength = null; // Clear any prior Content-Length
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@Resources.ErrorPageHtml_Title</title>
<style>
<%$ include: ErrorPage.css %>
</style>
</head>
<body>
<h1>@Resources.ErrorPageHtml_CompilationException</h1>
@if (Model.Options.ShowExceptionDetails)
{
<div class="titleerror">@errorDetail.Error.GetType().Name: @{ Output.Write(WebUtility.HtmlEncode(errorDetail.Error.Message).Replace("\r\n", "<br/>").Replace("\r", "<br/>").Replace("\n", "<br/>")); }</div>
}
else
{
<h2>@Resources.ErrorPageHtml_EnableShowExceptions</h2>
}
@if (Model.Options.ShowExceptionDetails)
{
<div id="stackpage" class="page">
@{ int tabIndex = 6; }
<br />
<ul>
@foreach (var frame in errorDetail.StackFrames)
{
<li class="frame" tabindex="@tabIndex">
@{ tabIndex++; }
@if (!string.IsNullOrEmpty(frame.ErrorDetails))
{
<h3>@frame.ErrorDetails</h3>
}
else
{
if (string.IsNullOrEmpty(frame.File))
{
<h3>@frame.Function</h3>
}
else
{
<h3>@frame.Function in <code title="@frame.File">@System.IO.Path.GetFileName(frame.File)</code></h3>
}
}
@if (frame.Line != 0 && frame.ContextCode.Any())
{
<div class="source">
@if (frame.PreContextCode != null)
{
<ol start="@frame.PreContextLine" class="collapsible">
@foreach (var line in frame.PreContextCode)
{
<li><span>@line</span></li>
}
</ol>
}
<ol start="@frame.Line" class="highlight">
@foreach (var line in frame.ContextCode)
{
<li><span>@line</span></li>
}
</ol>
@if (frame.PostContextCode != null)
{
<ol start='@(frame.Line + 1)' class="collapsible">
@foreach (var line in frame.PostContextCode)
{
<li><span>@line</span></li>
}
</ol>
}
</div>
}
</li>
}
</ul>
</div>
}
<script>
//<!--
<%$ include: ErrorPage.js %>
//-->
</script>
</body>
</html>

View File

@ -472,7 +472,7 @@ using Views
#line hidden
#line 123 "ErrorPage.cshtml"
if (frame.Line != 0 && frame.ContextCode != null)
if (frame.Line != 0 && frame.ContextCode.Any())
{
#line default
@ -493,8 +493,8 @@ using Views
#line hidden
WriteLiteral(" <ol");
WriteAttribute("start", Tuple.Create(" start=\"", 5277), Tuple.Create("\"", 5306),
Tuple.Create(Tuple.Create("", 5285), Tuple.Create<System.Object, System.Int32>(frame.PreContextLine, 5285), false));
WriteAttribute("start", Tuple.Create(" start=\"", 5275), Tuple.Create("\"", 5304),
Tuple.Create(Tuple.Create("", 5283), Tuple.Create<System.Object, System.Int32>(frame.PreContextLine, 5283), false));
WriteLiteral(" class=\"collapsible\">\r\n");
#line 129 "ErrorPage.cshtml"
@ -530,22 +530,43 @@ using Views
#line hidden
WriteLiteral("\r\n <ol");
WriteAttribute("start", Tuple.Create(" start=\"", 5774), Tuple.Create("\"", 5793),
Tuple.Create(Tuple.Create("", 5782), Tuple.Create<System.Object, System.Int32>(frame.Line, 5782), false));
WriteLiteral(" class=\"highlight\">\r\n <li><span>");
WriteAttribute("start", Tuple.Create(" start=\"", 5772), Tuple.Create("\"", 5791),
Tuple.Create(Tuple.Create("", 5780), Tuple.Create<System.Object, System.Int32>(frame.Line, 5780), false));
WriteLiteral(" class=\"highlight\">\r\n");
#line 137 "ErrorPage.cshtml"
Write(frame.ContextCode);
#line default
#line hidden
WriteLiteral("</span></li></ol>\r\n\r\n");
#line 139 "ErrorPage.cshtml"
#line default
#line hidden
#line 137 "ErrorPage.cshtml"
foreach (var line in frame.ContextCode)
{
#line default
#line hidden
WriteLiteral(" <li><span>");
#line 139 "ErrorPage.cshtml"
Write(line);
#line default
#line hidden
WriteLiteral("</span></li>\r\n");
#line 140 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </ol>\r\n\r\n");
#line 143 "ErrorPage.cshtml"
#line default
#line hidden
#line 143 "ErrorPage.cshtml"
if (frame.PostContextCode != null)
{
@ -553,16 +574,16 @@ using Views
#line hidden
WriteLiteral(" <ol");
WriteAttribute("start", Tuple.Create(" start=\'", 6091), Tuple.Create("\'", 6116),
Tuple.Create(Tuple.Create("", 6099), Tuple.Create<System.Object, System.Int32>(frame.Line + 1, 6099), false));
WriteAttribute("start", Tuple.Create(" start=\'", 6318), Tuple.Create("\'", 6343),
Tuple.Create(Tuple.Create("", 6326), Tuple.Create<System.Object, System.Int32>(frame.Line + 1, 6326), false));
WriteLiteral(" class=\"collapsible\">\r\n");
#line 142 "ErrorPage.cshtml"
#line 146 "ErrorPage.cshtml"
#line default
#line hidden
#line 142 "ErrorPage.cshtml"
#line 146 "ErrorPage.cshtml"
foreach (var line in frame.PostContextCode)
{
@ -570,55 +591,55 @@ using Views
#line hidden
WriteLiteral(" <li><span>");
#line 144 "ErrorPage.cshtml"
#line 148 "ErrorPage.cshtml"
Write(line);
#line default
#line hidden
WriteLiteral("</span></li>\r\n");
#line 145 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </ol>\r\n");
#line 147 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </div>\r\n");
#line 149 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </li>\r\n");
WriteLiteral(" </ol>\r\n");
#line 151 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </div>\r\n");
#line 153 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </li>\r\n");
#line 155 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </ul>\r\n </li>\r\n");
#line 154 "ErrorPage.cshtml"
#line 158 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </ul>\r\n </div>\r\n");
#line 157 "ErrorPage.cshtml"
#line 161 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" ");
#line 158 "ErrorPage.cshtml"
#line 162 "ErrorPage.cshtml"
if (Model.Options.ShowQuery)
{
@ -626,13 +647,13 @@ using Views
#line hidden
WriteLiteral(" <div id=\"querypage\" class=\"page\">\r\n");
#line 161 "ErrorPage.cshtml"
#line 165 "ErrorPage.cshtml"
#line default
#line hidden
#line 161 "ErrorPage.cshtml"
#line 165 "ErrorPage.cshtml"
if (Model.Query.Any())
{
@ -641,26 +662,26 @@ using Views
WriteLiteral(" <table>\r\n <thead>\r\n " +
" <tr>\r\n <th>");
#line 166 "ErrorPage.cshtml"
#line 170 "ErrorPage.cshtml"
Write(Resources.ErrorPageHtml_VariableColumn);
#line default
#line hidden
WriteLiteral("</th>\r\n <th>");
#line 167 "ErrorPage.cshtml"
#line 171 "ErrorPage.cshtml"
Write(Resources.ErrorPageHtml_ValueColumn);
#line default
#line hidden
WriteLiteral("</th>\r\n </tr>\r\n </thead>\r\n " +
" <tbody>\r\n");
#line 171 "ErrorPage.cshtml"
#line 175 "ErrorPage.cshtml"
#line default
#line hidden
#line 171 "ErrorPage.cshtml"
#line 175 "ErrorPage.cshtml"
foreach (var kv in Model.Query.OrderBy(kv => kv.Key))
{
foreach (var v in kv.Value)
@ -671,19 +692,19 @@ using Views
WriteLiteral(" <tr>\r\n " +
" <td>");
#line 176 "ErrorPage.cshtml"
#line 180 "ErrorPage.cshtml"
Write(kv.Key);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 177 "ErrorPage.cshtml"
#line 181 "ErrorPage.cshtml"
Write(v);
#line default
#line hidden
WriteLiteral("</td>\r\n </tr>\r\n");
#line 179 "ErrorPage.cshtml"
#line 183 "ErrorPage.cshtml"
}
}
@ -691,7 +712,7 @@ using Views
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n");
#line 183 "ErrorPage.cshtml"
#line 187 "ErrorPage.cshtml"
}
else
{
@ -700,27 +721,27 @@ using Views
#line hidden
WriteLiteral(" <p>");
#line 186 "ErrorPage.cshtml"
#line 190 "ErrorPage.cshtml"
Write(Resources.ErrorPageHtml_NoQueryStringData);
#line default
#line hidden
WriteLiteral("</p>\r\n");
#line 187 "ErrorPage.cshtml"
#line 191 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </div>\r\n");
#line 189 "ErrorPage.cshtml"
#line 193 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" ");
#line 190 "ErrorPage.cshtml"
#line 194 "ErrorPage.cshtml"
if (Model.Options.ShowCookies)
{
/* TODO:
@ -757,7 +778,7 @@ using Views
#line hidden
WriteLiteral(" ");
#line 221 "ErrorPage.cshtml"
#line 225 "ErrorPage.cshtml"
if (Model.Options.ShowHeaders)
{
@ -765,13 +786,13 @@ using Views
#line hidden
WriteLiteral(" <div id=\"headerspage\" class=\"page\">\r\n");
#line 224 "ErrorPage.cshtml"
#line 228 "ErrorPage.cshtml"
#line default
#line hidden
#line 224 "ErrorPage.cshtml"
#line 228 "ErrorPage.cshtml"
if (Model.Headers.Any())
{
@ -780,26 +801,26 @@ using Views
WriteLiteral(" <table>\r\n <thead>\r\n " +
" <tr>\r\n <th>");
#line 229 "ErrorPage.cshtml"
#line 233 "ErrorPage.cshtml"
Write(Resources.ErrorPageHtml_VariableColumn);
#line default
#line hidden
WriteLiteral("</th>\r\n <th>");
#line 230 "ErrorPage.cshtml"
#line 234 "ErrorPage.cshtml"
Write(Resources.ErrorPageHtml_ValueColumn);
#line default
#line hidden
WriteLiteral("</th>\r\n </tr>\r\n </thead>\r\n " +
" <tbody>\r\n");
#line 234 "ErrorPage.cshtml"
#line 238 "ErrorPage.cshtml"
#line default
#line hidden
#line 234 "ErrorPage.cshtml"
#line 238 "ErrorPage.cshtml"
foreach (var kv in Model.Headers.OrderBy(kv => kv.Key))
{
foreach (var v in kv.Value)
@ -810,19 +831,19 @@ using Views
WriteLiteral(" <tr>\r\n " +
" <td>");
#line 239 "ErrorPage.cshtml"
#line 243 "ErrorPage.cshtml"
Write(kv.Key);
#line default
#line hidden
WriteLiteral("</td>\r\n <td>");
#line 240 "ErrorPage.cshtml"
#line 244 "ErrorPage.cshtml"
Write(v);
#line default
#line hidden
WriteLiteral("</td>\r\n </tr>\r\n");
#line 242 "ErrorPage.cshtml"
#line 246 "ErrorPage.cshtml"
}
}
@ -830,7 +851,7 @@ using Views
#line hidden
WriteLiteral(" </tbody>\r\n </table>\r\n");
#line 246 "ErrorPage.cshtml"
#line 250 "ErrorPage.cshtml"
}
else
{
@ -839,27 +860,27 @@ using Views
#line hidden
WriteLiteral(" <p>");
#line 249 "ErrorPage.cshtml"
#line 253 "ErrorPage.cshtml"
Write(Resources.ErrorPageHtml_NoHeaderData);
#line default
#line hidden
WriteLiteral("</p>\r\n");
#line 250 "ErrorPage.cshtml"
#line 254 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </div>\r\n");
#line 252 "ErrorPage.cshtml"
#line 256 "ErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" ");
#line 253 "ErrorPage.cshtml"
#line 257 "ErrorPage.cshtml"
if (Model.Options.ShowEnvironment)
{
/* TODO:

View File

@ -120,7 +120,7 @@
<h3>@frame.Function in <code title="@frame.File">@System.IO.Path.GetFileName(frame.File)</code></h3>
}
@if (frame.Line != 0 && frame.ContextCode != null)
@if (frame.Line != 0 && frame.ContextCode.Any())
{
<div class="source">
@if (frame.PreContextCode != null)
@ -134,7 +134,11 @@
}
<ol start="@frame.Line" class="highlight">
<li><span>@frame.ContextCode</span></li></ol>
@foreach (var line in frame.ContextCode)
{
<li><span>@line</span></li>
}
</ol>
@if (frame.PostContextCode != null)
{

View File

@ -39,11 +39,16 @@ namespace Microsoft.AspNet.Diagnostics.Views
/// <summary>
///
/// </summary>
public string ContextCode { get; set; }
public IEnumerable<string> ContextCode { get; set; }
/// <summary>
///
/// </summary>
public IEnumerable<string> PostContextCode { get; set; }
/// <summary>
/// Specific error details for this stack frame.
/// </summary>
public string ErrorDetails { get; set; }
}
}