From 576731e86e322dd0f3d63efc66cda1b929315ad4 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 19 Dec 2014 15:48:37 -0800 Subject: [PATCH] Adding contract interfaces for specifying runtime compilation exceptions. Fixes #12 --- .../Compilation/ICompilationException.cs | 20 + .../Compilation/ICompilationFailure.cs | 40 ++ .../Compilation/ICompilationMessage.cs | 40 ++ .../ErrorPageMiddleware.cs | 83 +++- .../Properties/Resources.Designer.cs | 20 +- .../Resources.resx | 3 + .../Views/CompilationErrorModel.cs | 23 + .../Views/CompilationErrorPage.cs | 417 ++++++++++++++++++ .../Views/CompilationErrorPage.cshtml | 101 +++++ .../Views/ErrorPage.cs | 121 ++--- .../Views/ErrorPage.cshtml | 8 +- .../Views/StackFrame.cs | 7 +- 12 files changed, 822 insertions(+), 61 deletions(-) create mode 100644 src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationException.cs create mode 100644 src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationFailure.cs create mode 100644 src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationMessage.cs create mode 100644 src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorModel.cs create mode 100644 src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cs create mode 100644 src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cshtml diff --git a/src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationException.cs b/src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationException.cs new file mode 100644 index 0000000000..ba9102a35b --- /dev/null +++ b/src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationException.cs @@ -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 +{ + /// + /// Specifies the contract for an exception representing compilation failure. + /// + [AssemblyNeutral] + public interface ICompilationException + { + /// + /// Gets a sequence of with compilation failures. + /// + IEnumerable CompilationFailures { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationFailure.cs b/src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationFailure.cs new file mode 100644 index 0000000000..bf16458fc6 --- /dev/null +++ b/src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationFailure.cs @@ -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 +{ + /// + /// Specifies the contract for a file that fails compilation. + /// + [AssemblyNeutral] + public interface ICompilationFailure + { + /// + /// Path of the file that produced the compilation exception. + /// + string SourceFilePath { get; } + + /// + /// Contents of the file. + /// + string SourceFileContent { get; } + + /// + /// Contents being compiled. + /// + /// + /// For templated files, the represents the original content and + /// represents the transformed content. This property can be null if + /// the exception is encountered during transformation. + /// + string CompiledContent { get; } + + /// + /// Gets a sequence of produced as a result of compilation. + /// + IEnumerable Messages { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationMessage.cs b/src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationMessage.cs new file mode 100644 index 0000000000..ed3ca5fed8 --- /dev/null +++ b/src/Microsoft.AspNet.Diagnostics/Compilation/ICompilationMessage.cs @@ -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 +{ + /// + /// Specifies the contract for diagnostic messages produced as result of compiling an instance + /// of . + /// + [AssemblyNeutral] + public interface ICompilationMessage + { + /// + /// Gets the error message. + /// + string Message { get; } + + /// + /// Gets the zero-based line index for the start of the compilation error. + /// + int StartLine { get; } + + /// + /// Gets the zero-based column index for the start of the compilation error. + /// + int StartColumn { get; } + + /// + /// Gets the zero-based line index for the end of the compilation error. + /// + int EndLine { get; } + + /// + /// Gets the zero-based column index for the end of the compilation error. + /// + int EndColumn { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Diagnostics/ErrorPageMiddleware.cs b/src/Microsoft.AspNet.Diagnostics/ErrorPageMiddleware.cs index d872d65f04..aa8851bcc0 100644 --- a/src/Microsoft.AspNet.Diagnostics/ErrorPageMiddleware.cs +++ b/src/Microsoft.AspNet.Diagnostics/ErrorPageMiddleware.cs @@ -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(); + 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 GetErrorDetails(Exception ex, bool showSource) @@ -160,14 +223,22 @@ namespace Microsoft.AspNet.Diagnostics if (showSource && File.Exists(file)) { IEnumerable 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 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; } diff --git a/src/Microsoft.AspNet.Diagnostics/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Diagnostics/Properties/Resources.Designer.cs index 151b1aa94f..9ac48701ad 100644 --- a/src/Microsoft.AspNet.Diagnostics/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Diagnostics/Properties/Resources.Designer.cs @@ -379,7 +379,7 @@ namespace Microsoft.AspNet.Diagnostics } /// - /// Name + /// Version /// internal static string RuntimeInfoPage_PackageVersionColumnName { @@ -387,7 +387,7 @@ namespace Microsoft.AspNet.Diagnostics } /// - /// Name + /// Version /// internal static string FormatRuntimeInfoPage_PackageVersionColumnName() { @@ -570,6 +570,22 @@ namespace Microsoft.AspNet.Diagnostics return GetString("WelcomeTitle"); } + /// + /// 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. + /// + internal static string ErrorPageHtml_CompilationException + { + get { return GetString("ErrorPageHtml_CompilationException"); } + } + + /// + /// 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. + /// + internal static string FormatErrorPageHtml_CompilationException() + { + return GetString("ErrorPageHtml_CompilationException"); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNet.Diagnostics/Resources.resx b/src/Microsoft.AspNet.Diagnostics/Resources.resx index a56b82ed1d..98190423f4 100644 --- a/src/Microsoft.AspNet.Diagnostics/Resources.resx +++ b/src/Microsoft.AspNet.Diagnostics/Resources.resx @@ -230,4 +230,7 @@ Your ASP.NET vNext application has been successfully started. + + 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. + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorModel.cs b/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorModel.cs new file mode 100644 index 0000000000..fed8231c1b --- /dev/null +++ b/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorModel.cs @@ -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 +{ + /// + /// Holds data to be displayed on the compilation error page. + /// + public class CompilationErrorPageModel + { + /// + /// Options for what output to display. + /// + public ErrorPageOptions Options { get; set; } + + /// + /// Detailed information about each parse or compilation error. + /// + public ErrorDetails ErrorDetails { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cs b/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cs new file mode 100644 index 0000000000..c2647b0698 --- /dev/null +++ b/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cs @@ -0,0 +1,417 @@ +namespace Microsoft.AspNet.Diagnostics.Views +{ +#line 1 "CompilationErrorPage.cshtml" +using System + +#line default +#line hidden + ; +#line 2 "CompilationErrorPage.cshtml" +using System.Globalization + +#line default +#line hidden + ; +#line 3 "CompilationErrorPage.cshtml" +using System.Linq + +#line default +#line hidden + ; +#line 4 "CompilationErrorPage.cshtml" +using System.Net + +#line default +#line hidden + ; +#line 5 "CompilationErrorPage.cshtml" +using Views + +#line default +#line hidden + ; + using System.Threading.Tasks; + + public class CompilationErrorPage : Microsoft.AspNet.Diagnostics.Views.BaseView + { +#line 7 "CompilationErrorPage.cshtml" + + public CompilationErrorPageModel Model { get; set; } + +#line default +#line hidden + #line hidden + public CompilationErrorPage() + { + } + + #pragma warning disable 1998 + public override async Task ExecuteAsync() + { +#line 10 "CompilationErrorPage.cshtml" + + var errorDetail = Model.ErrorDetails; + + Response.StatusCode = 500; + Response.ContentType = "text/html"; + Response.ContentLength = null; // Clear any prior Content-Length + +#line default +#line hidden + + WriteLiteral("\r\n\r\n\r\n \r\n \r\n " + +""); +#line 21 "CompilationErrorPage.cshtml" + Write(Resources.ErrorPageHtml_Title); + +#line default +#line hidden + WriteLiteral("\r\n + + +

@Resources.ErrorPageHtml_CompilationException

+ @if (Model.Options.ShowExceptionDetails) + { +
@errorDetail.Error.GetType().Name: @{ Output.Write(WebUtility.HtmlEncode(errorDetail.Error.Message).Replace("\r\n", "
").Replace("\r", "
").Replace("\n", "
")); }
+ } + else + { +

@Resources.ErrorPageHtml_EnableShowExceptions

+ } + @if (Model.Options.ShowExceptionDetails) + { +
+ @{ int tabIndex = 6; } +
+
    + @foreach (var frame in errorDetail.StackFrames) + { +
  • + @{ tabIndex++; } + @if (!string.IsNullOrEmpty(frame.ErrorDetails)) + { +

    @frame.ErrorDetails

    + } + else + { + if (string.IsNullOrEmpty(frame.File)) + { +

    @frame.Function

    + } + else + { +

    @frame.Function in @System.IO.Path.GetFileName(frame.File)

    + } + } + @if (frame.Line != 0 && frame.ContextCode.Any()) + { +
    + @if (frame.PreContextCode != null) + { +
      + @foreach (var line in frame.PreContextCode) + { +
    1. @line
    2. + } +
    + } +
      + @foreach (var line in frame.ContextCode) + { +
    1. @line
    2. + } +
    + @if (frame.PostContextCode != null) + { +
      + @foreach (var line in frame.PostContextCode) + { +
    1. @line
    2. + } +
    + } +
    + } +
  • + } +
+
+ } + + + diff --git a/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cs b/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cs index a2826d6d32..ecb486deae 100644 --- a/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cs +++ b/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cs @@ -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(" (frame.PreContextLine, 5285), false)); + WriteAttribute("start", Tuple.Create(" start=\"", 5275), Tuple.Create("\"", 5304), + Tuple.Create(Tuple.Create("", 5283), Tuple.Create(frame.PreContextLine, 5283), false)); WriteLiteral(" class=\"collapsible\">\r\n"); #line 129 "ErrorPage.cshtml" @@ -530,22 +530,43 @@ using Views #line hidden WriteLiteral("\r\n (frame.Line, 5782), false)); - WriteLiteral(" class=\"highlight\">\r\n
  • "); + WriteAttribute("start", Tuple.Create(" start=\"", 5772), Tuple.Create("\"", 5791), + Tuple.Create(Tuple.Create("", 5780), Tuple.Create(frame.Line, 5780), false)); + WriteLiteral(" class=\"highlight\">\r\n"); #line 137 "ErrorPage.cshtml" - Write(frame.ContextCode); + #line default #line hidden - WriteLiteral("
  • \r\n\r\n"); + +#line 137 "ErrorPage.cshtml" + foreach (var line in frame.ContextCode) + { + +#line default +#line hidden + + WriteLiteral("
  • "); #line 139 "ErrorPage.cshtml" + Write(line); + +#line default +#line hidden + WriteLiteral("
  • \r\n"); +#line 140 "ErrorPage.cshtml" + } + +#line default +#line hidden + + WriteLiteral(" \r\n\r\n"); +#line 143 "ErrorPage.cshtml" #line default #line hidden -#line 139 "ErrorPage.cshtml" +#line 143 "ErrorPage.cshtml" if (frame.PostContextCode != null) { @@ -553,16 +574,16 @@ using Views #line hidden WriteLiteral(" (frame.Line + 1, 6099), false)); + WriteAttribute("start", Tuple.Create(" start=\'", 6318), Tuple.Create("\'", 6343), + Tuple.Create(Tuple.Create("", 6326), Tuple.Create(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("
  • "); -#line 144 "ErrorPage.cshtml" +#line 148 "ErrorPage.cshtml" Write(line); #line default #line hidden WriteLiteral("
  • \r\n"); -#line 145 "ErrorPage.cshtml" +#line 149 "ErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n"); -#line 147 "ErrorPage.cshtml" +#line 151 "ErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n"); -#line 149 "ErrorPage.cshtml" +#line 153 "ErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n"); -#line 151 "ErrorPage.cshtml" +#line 155 "ErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n \r\n"); -#line 154 "ErrorPage.cshtml" +#line 158 "ErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n \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("
    \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(" \r\n \r\n " + " \r\n \r\n \r\n \r\n \r\n " + " \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(" \r\n " + " \r\n \r\n \r\n"); -#line 179 "ErrorPage.cshtml" +#line 183 "ErrorPage.cshtml" } } @@ -691,7 +712,7 @@ using Views #line hidden WriteLiteral(" \r\n
    "); -#line 166 "ErrorPage.cshtml" +#line 170 "ErrorPage.cshtml" Write(Resources.ErrorPageHtml_VariableColumn); #line default #line hidden WriteLiteral(""); -#line 167 "ErrorPage.cshtml" +#line 171 "ErrorPage.cshtml" Write(Resources.ErrorPageHtml_ValueColumn); #line default #line hidden WriteLiteral("
    "); -#line 176 "ErrorPage.cshtml" +#line 180 "ErrorPage.cshtml" Write(kv.Key); #line default #line hidden WriteLiteral(""); -#line 177 "ErrorPage.cshtml" +#line 181 "ErrorPage.cshtml" Write(v); #line default #line hidden WriteLiteral("
    \r\n"); -#line 183 "ErrorPage.cshtml" +#line 187 "ErrorPage.cshtml" } else { @@ -700,27 +721,27 @@ using Views #line hidden WriteLiteral("

    "); -#line 186 "ErrorPage.cshtml" +#line 190 "ErrorPage.cshtml" Write(Resources.ErrorPageHtml_NoQueryStringData); #line default #line hidden WriteLiteral("

    \r\n"); -#line 187 "ErrorPage.cshtml" +#line 191 "ErrorPage.cshtml" } #line default #line hidden WriteLiteral("
    \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("
    \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(" \r\n \r\n " + " \r\n \r\n \r\n \r\n \r\n " + " \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(" \r\n " + " \r\n \r\n \r\n"); -#line 242 "ErrorPage.cshtml" +#line 246 "ErrorPage.cshtml" } } @@ -830,7 +851,7 @@ using Views #line hidden WriteLiteral(" \r\n
    "); -#line 229 "ErrorPage.cshtml" +#line 233 "ErrorPage.cshtml" Write(Resources.ErrorPageHtml_VariableColumn); #line default #line hidden WriteLiteral(""); -#line 230 "ErrorPage.cshtml" +#line 234 "ErrorPage.cshtml" Write(Resources.ErrorPageHtml_ValueColumn); #line default #line hidden WriteLiteral("
    "); -#line 239 "ErrorPage.cshtml" +#line 243 "ErrorPage.cshtml" Write(kv.Key); #line default #line hidden WriteLiteral(""); -#line 240 "ErrorPage.cshtml" +#line 244 "ErrorPage.cshtml" Write(v); #line default #line hidden WriteLiteral("
    \r\n"); -#line 246 "ErrorPage.cshtml" +#line 250 "ErrorPage.cshtml" } else { @@ -839,27 +860,27 @@ using Views #line hidden WriteLiteral("

    "); -#line 249 "ErrorPage.cshtml" +#line 253 "ErrorPage.cshtml" Write(Resources.ErrorPageHtml_NoHeaderData); #line default #line hidden WriteLiteral("

    \r\n"); -#line 250 "ErrorPage.cshtml" +#line 254 "ErrorPage.cshtml" } #line default #line hidden WriteLiteral("
    \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: diff --git a/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cshtml b/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cshtml index de7abfad3c..7a0075ac42 100644 --- a/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cshtml +++ b/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cshtml @@ -120,7 +120,7 @@

    @frame.Function in @System.IO.Path.GetFileName(frame.File)

    } - @if (frame.Line != 0 && frame.ContextCode != null) + @if (frame.Line != 0 && frame.ContextCode.Any()) {
    @if (frame.PreContextCode != null) @@ -134,7 +134,11 @@ }
      -
    1. @frame.ContextCode
    + @foreach (var line in frame.ContextCode) + { +
  • @line
  • + } + @if (frame.PostContextCode != null) { diff --git a/src/Microsoft.AspNet.Diagnostics/Views/StackFrame.cs b/src/Microsoft.AspNet.Diagnostics/Views/StackFrame.cs index 2b17068868..4a1ec2f513 100644 --- a/src/Microsoft.AspNet.Diagnostics/Views/StackFrame.cs +++ b/src/Microsoft.AspNet.Diagnostics/Views/StackFrame.cs @@ -39,11 +39,16 @@ namespace Microsoft.AspNet.Diagnostics.Views /// /// /// - public string ContextCode { get; set; } + public IEnumerable ContextCode { get; set; } /// /// /// public IEnumerable PostContextCode { get; set; } + + /// + /// Specific error details for this stack frame. + /// + public string ErrorDetails { get; set; } } }