Revert "Remove existing implementation of StartupExceptionPage and use the one in Common"

This reverts commit 83e0d4798e.
This commit is contained in:
Kiran Challa 2016-08-25 17:21:29 -07:00
parent 83e0d4798e
commit 3f6b558cf3
11 changed files with 340 additions and 1891 deletions

View File

@ -5,20 +5,16 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Hosting.Views;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.StackTrace.Sources;
namespace Microsoft.AspNetCore.Hosting.Internal
{
@ -172,44 +168,15 @@ namespace Microsoft.AspNetCore.Hosting.Internal
// Generate an HTML error page.
var hostingEnv = _applicationServices.GetRequiredService<IHostingEnvironment>();
var showDetailedErrors = hostingEnv.IsDevelopment() || _options.DetailedErrors;
var errorBytes = StartupExceptionPage.GenerateErrorHtml(showDetailedErrors, ex);
var model = new ErrorPageModel();
var runtimeType = Microsoft.Extensions.Internal.RuntimeEnvironment.RuntimeType;
model.RuntimeDisplayName = (runtimeType == "CoreCLR") ? ".NET Core" : runtimeType == "CLR" ? ".NET Framework" : "Mono";
#if NETSTANDARD1_3
var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).GetTypeInfo().Assembly;
var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString();
var clrVersion = assemblyVersion;
#else
var clrVersion = Environment.Version.ToString();
#endif
model.RuntimeArchitecture = RuntimeInformation.ProcessArchitecture.ToString();
var currentAssembly = typeof(ErrorPage).GetTypeInfo().Assembly;
model.CurrentAssemblyVesion = currentAssembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
.InformationalVersion;
model.ClrVersion = clrVersion;
model.OperatingSystemDescription = RuntimeInformation.OSDescription;
if (showDetailedErrors)
{
var exceptionDetailProvider = new ExceptionDetailsProvider(
hostingEnv.ContentRootFileProvider,
sourceCodeLineCount: 6);
model.ErrorDetails = exceptionDetailProvider.GetDetails(ex);
}
else
{
model.ErrorDetails = new ExceptionDetails[0];
}
var errorPage = new ErrorPage(model);
return context =>
{
context.Response.StatusCode = 500;
context.Response.Headers["Cache-Control"] = "no-cache";
return errorPage.ExecuteAsync(context);
context.Response.Headers["Cache-Control"] = "private, max-age=0";
context.Response.ContentType = "text/html; charset=utf-8";
context.Response.ContentLength = errorBytes.Length;
return context.Response.Body.WriteAsync(errorBytes, 0, errorBytes.Length);
};
}
}

View File

@ -1,78 +0,0 @@
// <auto-generated />
namespace Microsoft.AspNetCore.Hosting
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNetCore.Hosting.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// Internal Server Error
/// </summary>
internal static string ErrorPageHtml_Title
{
get { return GetString("ErrorPageHtml_Title"); }
}
/// <summary>
/// Internal Server Error
/// </summary>
internal static string FormatErrorPageHtml_Title()
{
return GetString("ErrorPageHtml_Title");
}
/// <summary>
/// An error occurred while starting the application.
/// </summary>
internal static string ErrorPageHtml_UnhandledException
{
get { return GetString("ErrorPageHtml_UnhandledException"); }
}
/// <summary>
/// An error occurred while starting the application.
/// </summary>
internal static string FormatErrorPageHtml_UnhandledException()
{
return GetString("ErrorPageHtml_UnhandledException");
}
/// <summary>
/// Unknown location
/// </summary>
internal static string ErrorPageHtml_UnknownLocation
{
get { return GetString("ErrorPageHtml_UnknownLocation"); }
}
/// <summary>
/// Unknown location
/// </summary>
internal static string FormatErrorPageHtml_UnknownLocation()
{
return GetString("ErrorPageHtml_UnknownLocation");
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
System.Diagnostics.Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
}
}

View File

@ -1,129 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ErrorPageHtml_Title" xml:space="preserve">
<value>Internal Server Error</value>
</data>
<data name="ErrorPageHtml_UnhandledException" xml:space="preserve">
<value>An error occurred while starting the application.</value>
</data>
<data name="ErrorPageHtml_UnknownLocation" xml:space="preserve">
<value>Unknown location</value>
</data>
</root>

View File

@ -1,162 +0,0 @@
@using System
@using System.Globalization
@using System.Linq
@using System.Net
@using System.Reflection
@using Microsoft.AspNetCore.Hosting.Views
@functions
{
public ErrorPage(ErrorPageModel model)
{
Model = model;
}
public ErrorPageModel Model { get; set; }
}
@{
Response.ContentType = "text/html; charset=utf-8";
var location = string.Empty;
}
<!DOCTYPE html>
<html lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>@Resources.ErrorPageHtml_Title</title>
<style>
<%$ include: ErrorPage.css %>
</style>
</head>
<body>
<h1>@Resources.ErrorPageHtml_UnhandledException</h1>
@foreach (var errorDetail in Model.ErrorDetails)
{
<div class="titleerror">@errorDetail.Error.GetType().Name: @{ Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); }</div>
@{
var firstFrame = errorDetail.StackFrames.FirstOrDefault();
if (firstFrame != null)
{
location = firstFrame.Function;
}
}
if (!string.IsNullOrEmpty(location) && firstFrame != null && !string.IsNullOrEmpty(firstFrame.File))
{
<p class="location">@location in <code title="@firstFrame.File">@System.IO.Path.GetFileName(firstFrame.File)</code>, line @firstFrame.Line</p>
}
else if (!string.IsNullOrEmpty(location))
{
<p class="location">@location</p>
}
else
{
<p class="location">@Resources.ErrorPageHtml_UnknownLocation</p>
}
var reflectionTypeLoadException = errorDetail.Error as ReflectionTypeLoadException;
if (reflectionTypeLoadException != null)
{
if (reflectionTypeLoadException.LoaderExceptions.Length > 0)
{
<h3>Loader Exceptions:</h3>
<ul>
@foreach (var ex in reflectionTypeLoadException.LoaderExceptions)
{
<li>@ex.Message</li>
}
</ul>
}
}
}
<div id="stackpage" class="page">
<ul>
@{
var exceptionCount = 0;
var stackFrameCount = 0;
var exceptionDetailId = "";
var frameId = "";
}
@foreach (var errorDetail in Model.ErrorDetails)
{
@{
exceptionCount++;
exceptionDetailId = "exceptionDetail" + exceptionCount;
}
<li>
<h2 class="stackerror">@errorDetail.Error.GetType().Name: @errorDetail.Error.Message</h2>
<ul>
@foreach (var frame in errorDetail.StackFrames)
{
@{
stackFrameCount++;
frameId = "frame" + stackFrameCount;
}
<li class="frame" id="@frameId">
@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())
{
<button class="expandCollapseButton" data-frameId="@frameId">+</button>
<div class="source">
@if (frame.PreContextCode.Any())
{
<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.Any())
{
<ol start='@(frame.Line + 1)' class="collapsible">
@foreach (var line in frame.PostContextCode)
{
<li><span>@line</span></li>
}
</ol>
}
</div>
}
</li>
}
</ul>
</li>
<li>
<br/>
<div class="rawExceptionBlock">
<div class="showRawExceptionContainer">
<button class="showRawException" data-exceptionDetailId="@exceptionDetailId">Show raw exception details</button>
</div>
<div id="@exceptionDetailId" class="rawExceptionDetails">
<pre class="rawExceptionStackTrace">@errorDetail.Error.ToString()</pre>
</div>
</div>
</li>
}
</ul>
</div>
<footer>
@Model.RuntimeDisplayName @Model.RuntimeArchitecture v@(Model.ClrVersion) &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;Microsoft.AspNetCore.Hosting version @Model.CurrentAssemblyVesion &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp; @Model.OperatingSystemDescription &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="http://go.microsoft.com/fwlink/?LinkId=517394">Need help?</a>
</footer>
<script>
//<!--
<%$ include: ErrorPage.js %>
//-->
</script>
</body>
</html>

View File

@ -1,195 +0,0 @@
body {
font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
font-size: .813em;
color: #222;
}
h1, h2, h3, h4, h5 {
/*font-family: 'Segoe UI',Tahoma,Arial,Helvetica,sans-serif;*/
font-weight: 100;
}
h1 {
color: #44525e;
margin: 15px 0 15px 0;
}
h2 {
margin: 10px 5px 0 0;
}
h3 {
color: #363636;
margin: 5px 5px 0 0;
}
code {
font-family: Consolas, "Courier New", courier, monospace;
}
body .titleerror {
padding: 3px 3px 6px 3px;
display: block;
font-size: 1.5em;
font-weight: 100;
}
body .location {
margin: 3px 0 10px 30px;
}
#header {
font-size: 18px;
padding: 15px 0;
border-top: 1px #ddd solid;
border-bottom: 1px #ddd solid;
margin-bottom: 0;
}
#header li {
display: inline;
margin: 5px;
padding: 5px;
color: #a0a0a0;
cursor: pointer;
}
#header .selected {
background: #44c5f2;
color: #fff;
}
#stackpage ul {
list-style: none;
padding-left: 0;
margin: 0;
/*border-bottom: 1px #ddd solid;*/
}
#stackpage .details {
font-size: 1.2em;
padding: 3px;
color: #000;
}
#stackpage .stackerror {
padding: 5px;
border-bottom: 1px #ddd solid;
}
#stackpage .frame {
padding: 0;
margin: 0 0 0 30px;
}
#stackpage .frame h3 {
padding: 2px;
margin: 0;
}
#stackpage .source {
padding: 0 0 0 30px;
}
#stackpage .source ol li {
font-family: Consolas, "Courier New", courier, monospace;
white-space: pre;
background-color: #fbfbfb;
}
#stackpage .frame .source .highlight li span {
color: #FF0000;
}
#stackpage .source ol.collapsible li {
color: #888;
}
#stackpage .source ol.collapsible li span {
color: #606060;
}
.page table {
border-collapse: separate;
border-spacing: 0;
margin: 0 0 20px;
}
.page th {
vertical-align: bottom;
padding: 10px 5px 5px 5px;
font-weight: 400;
color: #a0a0a0;
text-align: left;
}
.page td {
padding: 3px 10px;
}
.page th, .page td {
border-right: 1px #ddd solid;
border-bottom: 1px #ddd solid;
border-left: 1px transparent solid;
border-top: 1px transparent solid;
box-sizing: border-box;
}
.page th:last-child, .page td:last-child {
border-right: 1px transparent solid;
}
.page .length {
text-align: right;
}
a {
color: #1ba1e2;
text-decoration: none;
}
a:hover {
color: #13709e;
text-decoration: underline;
}
.showRawException {
cursor: pointer;
color: #44c5f2;
background-color: transparent;
font-size: 1.2em;
text-align: left;
text-decoration: none;
display: inline-block;
border: 0;
padding: 0;
}
.rawExceptionStackTrace {
font-size: 1.2em;
}
.rawExceptionBlock {
border-top: 1px #ddd solid;
border-bottom: 1px #ddd solid;
}
.showRawExceptionContainer {
margin-top: 10px;
margin-bottom: 10px;
}
.expandCollapseButton {
cursor: pointer;
float: left;
height: 16px;
width: 16px;
font-size: 10px;
position: absolute;
left: 10px;
background-color: #eee;
padding: 0;
border: 0;
margin: 0;
}

View File

@ -1,192 +0,0 @@
(function (window, undefined) {
"use strict";
function ns(selector, element) {
return new NodeCollection(selector, element);
}
function NodeCollection(selector, element) {
this.items = [];
element = element || window.document;
var nodeList;
if (typeof (selector) === "string") {
nodeList = element.querySelectorAll(selector);
for (var i = 0, l = nodeList.length; i < l; i++) {
this.items.push(nodeList.item(i));
}
}
}
NodeCollection.prototype = {
each: function (callback) {
for (var i = 0, l = this.items.length; i < l; i++) {
callback(this.items[i], i);
}
return this;
},
children: function (selector) {
var children = [];
this.each(function (el) {
children = children.concat(ns(selector, el).items);
});
return ns(children);
},
hide: function () {
this.each(function (el) {
el.style.display = "none";
});
return this;
},
toggle: function () {
this.each(function (el) {
el.style.display = el.style.display === "none" ? "" : "none";
});
return this;
},
show: function () {
this.each(function (el) {
el.style.display = "";
});
return this;
},
addClass: function (className) {
this.each(function (el) {
var existingClassName = el.className,
classNames;
if (!existingClassName) {
el.className = className;
} else {
classNames = existingClassName.split(" ");
if (classNames.indexOf(className) < 0) {
el.className = existingClassName + " " + className;
}
}
});
return this;
},
removeClass: function (className) {
this.each(function (el) {
var existingClassName = el.className,
classNames, index;
if (existingClassName === className) {
el.className = "";
} else if (existingClassName) {
classNames = existingClassName.split(" ");
index = classNames.indexOf(className);
if (index > 0) {
classNames.splice(index, 1);
el.className = classNames.join(" ");
}
}
});
return this;
},
attr: function (name) {
if (this.items.length === 0) {
return null;
}
return this.items[0].getAttribute(name);
},
on: function (eventName, handler) {
this.each(function (el, idx) {
var callback = function (e) {
e = e || window.event;
if (!e.which && e.keyCode) {
e.which = e.keyCode; // Normalize IE8 key events
}
handler.apply(el, [e]);
};
if (el.addEventListener) { // DOM Events
el.addEventListener(eventName, callback, false);
} else if (el.attachEvent) { // IE8 events
el.attachEvent("on" + eventName, callback);
} else {
el["on" + type] = callback;
}
});
return this;
},
click: function (handler) {
return this.on("click", handler);
},
keypress: function (handler) {
return this.on("keypress", handler);
}
};
function frame(el) {
ns(".source .collapsible", el).toggle();
}
function expandCollapseButton(el) {
var frameId = el.getAttribute("data-frameId");
frame(document.getElementById(frameId));
if (el.innerText === "+") {
el.innerText = "-";
}
else {
el.innerText = "+";
}
}
function tab(el) {
var unselected = ns("#header .selected").removeClass("selected").attr("id");
var selected = ns("#" + el.id).addClass("selected").attr("id");
ns("#" + unselected + "page").hide();
ns("#" + selected + "page").show();
}
ns(".rawExceptionDetails").hide();
ns(".collapsible").hide();
ns(".page").hide();
ns("#stackpage").show();
ns(".expandCollapseButton")
.click(function () {
expandCollapseButton(this);
})
.keypress(function (e) {
if (e.which === 13) {
expandCollapseButton(this);
}
});
ns("#header li")
.click(function () {
tab(this);
})
.keypress(function (e) {
if (e.which === 13) {
tab(this);
}
});
ns(".showRawException")
.click(function () {
var exceptionDetailId = this.getAttribute("data-exceptionDetailId");
ns("#" + exceptionDetailId).toggle();
});
})(window);

View File

@ -1,29 +0,0 @@
// Copyright (c) .NET Foundation. 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.Extensions.StackTrace.Sources;
namespace Microsoft.AspNetCore.Hosting.Views
{
/// <summary>
/// Holds data to be displayed on the error page.
/// </summary>
internal class ErrorPageModel
{
/// <summary>
/// Detailed information about each exception in the stack.
/// </summary>
public IEnumerable<ExceptionDetails> ErrorDetails { get; set; }
public string RuntimeDisplayName { get; set; }
public string RuntimeArchitecture { get; set; }
public string ClrVersion { get; set; }
public string CurrentAssemblyVesion { get; set; }
public string OperatingSystemDescription { get; set; }
}
}

View File

@ -0,0 +1,329 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.Extensions.StackTrace.Sources;
namespace Microsoft.AspNetCore.Hosting
{
internal static class StartupExceptionPage
{
private static readonly string _errorPageFormatString = GetResourceString("GenericError.html", escapeBraces: true);
private static readonly string _errorMessageFormatString = GetResourceString("GenericError_Message.html");
private static readonly string _errorExceptionFormatString = GetResourceString("GenericError_Exception.html");
private static readonly string _errorFooterFormatString = GetResourceString("GenericError_Footer.html");
public static byte[] GenerateErrorHtml(bool showDetails, Exception exception)
{
// Build the message for each error
var builder = new StringBuilder();
var rawExceptionDetails = new StringBuilder();
if (!showDetails)
{
WriteMessage("An error occurred while starting the application.", builder);
}
else
{
Debug.Assert(exception != null);
var wasSourceCodeWrittenOntoPage = false;
var flattenedExceptions = FlattenAndReverseExceptionTree(exception);
foreach (var innerEx in flattenedExceptions)
{
WriteException(innerEx, builder, ref wasSourceCodeWrittenOntoPage);
}
WriteRawExceptionDetails("Show raw exception details", exception.ToString(), rawExceptionDetails);
}
// Generate the footer
var footer = showDetails ? GenerateFooterEncoded() : null;
// And generate the full markup
return Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, _errorPageFormatString, builder, rawExceptionDetails, footer));
}
private static string BuildCodeSnippetDiv(StackFrameInfo frameInfo)
{
var filename = frameInfo.FilePath;
if (!string.IsNullOrEmpty(filename))
{
int failingLineNumber = frameInfo.LineNumber;
if (failingLineNumber >= 1)
{
var lines = GetFailingCallSiteInFile(filename, failingLineNumber);
if (lines != null)
{
return @"<div class=""codeSnippet"">"
+ @"<div class=""filename""><code>" + HtmlEncodeAndReplaceLineBreaks(filename) + "</code></div>" + Environment.NewLine
+ string.Join(Environment.NewLine, lines) + "</div>" + Environment.NewLine;
}
}
}
// fallback
return null;
}
private static string BuildLineForStackFrame(StackFrameInfo frameInfo)
{
var builder = new StringBuilder("<pre>");
var stackFrame = frameInfo.StackFrame;
var method = stackFrame.GetMethod();
var displayInfo = frameInfo.MethodDisplayInfo;
// Special case: no method available
if (method == null)
{
return null;
}
// First, write the type name
var type = method.DeclaringType;
if (type != null)
{
// Special-case ExceptionDispatchInfo.Throw()
if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw")
{
return @"<pre><span class=""faded"">--- exception rethrown ---</span></pre>";
}
var typeName = displayInfo.DeclaringTypeName;
// Separate the namespace component from the type name so that we can format it differently.
if (!string.IsNullOrEmpty(typeName) && !string.IsNullOrEmpty(type.Namespace))
{
builder.Append($@"<span class=""faded"">at {HtmlEncodeAndReplaceLineBreaks(type.Namespace)}</span>");
typeName = typeName.Substring(type.Namespace.Length);
}
builder.Append(HtmlEncodeAndReplaceLineBreaks(typeName));
}
// Next, write the method signature
builder.Append(displayInfo.Name);
// Build method parameters
var methodParameters = "(" + string.Join(", ", displayInfo.Parameters) + ")";
builder.Append($@"<span class=""faded"">{HtmlEncodeAndReplaceLineBreaks(methodParameters)}</span>");
// Do we have source information for this frame?
if (stackFrame.GetILOffset() != -1)
{
var filename = frameInfo.FilePath;
if (!string.IsNullOrEmpty(filename))
{
builder.AppendFormat(CultureInfo.InvariantCulture, " in {0}:line {1:D}", HtmlEncodeAndReplaceLineBreaks(filename), frameInfo.LineNumber);
}
}
// Finish
builder.Append("</pre>");
return builder.ToString();
}
private static string GetResourceString(string name, bool escapeBraces = false)
{
// '{' and '}' are special in CSS, so we use "[[[0]]]" instead for {0} (and so on).
var assembly = typeof(StartupExceptionPage).GetTypeInfo().Assembly;
var resourceName = assembly.GetName().Name + ".compiler.resources." + name;
var manifestStream = assembly.GetManifestResourceStream(resourceName);
var formatString = new StreamReader(manifestStream, Encoding.UTF8, detectEncodingFromByteOrderMarks: false).ReadToEnd();
if (escapeBraces)
{
formatString = formatString.Replace("{", "{{").Replace("}", "}}").Replace("[[[", "{").Replace("]]]", "}");
}
return formatString;
}
private static List<string> GetFailingCallSiteInFile(string filename, int failedLineNumber)
{
// We figure out the [first, last] range of lines to read from the file.
var firstLineNumber = failedLineNumber - 2;
firstLineNumber = Math.Max(1, firstLineNumber);
var lastLineNumber = failedLineNumber + 2;
lastLineNumber = Math.Max(lastLineNumber, failedLineNumber);
// Figure out how many characters lastLineNumber will take to print.
var lastLineNumberCharLength = lastLineNumber.ToString("D", CultureInfo.InvariantCulture).Length;
var errorSubContents = new List<string>();
var didReadFailingLine = false;
try
{
var thisLineNum = 0;
foreach (var line in File.ReadLines(filename))
{
thisLineNum++;
// Are we within the correct range?
if (thisLineNum < firstLineNumber)
{
continue;
}
if (thisLineNum > lastLineNumber)
{
break;
}
var encodedLine = HtmlEncodeAndReplaceLineBreaks("Line "
+ thisLineNum.ToString("D", CultureInfo.InvariantCulture).PadLeft(lastLineNumberCharLength)
+ ": "
+ line);
if (thisLineNum == failedLineNumber)
{
didReadFailingLine = true;
errorSubContents.Add(@"<div class=""line error""><code>" + encodedLine + "</code></div>");
}
else
{
errorSubContents.Add(@"<div class=""line""><code>" + encodedLine + "</code></div>");
}
}
}
catch
{
// If there's an error for any reason, don't show source.
return null;
}
return (didReadFailingLine) ? errorSubContents : null;
}
private static string GenerateFooterEncoded()
{
var runtimeType = HtmlEncodeAndReplaceLineBreaks(Microsoft.Extensions.Internal.RuntimeEnvironment.RuntimeType);
var runtimeDisplayName = runtimeType == "CoreCLR" ? ".NET Core" : runtimeType == "CLR" ? ".NET Framework" : "Mono";
#if NETSTANDARD1_3
var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).GetTypeInfo().Assembly;
var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString();
var clrVersion = HtmlEncodeAndReplaceLineBreaks(assemblyVersion);
#else
var clrVersion = HtmlEncodeAndReplaceLineBreaks(Environment.Version.ToString());
#endif
var runtimeArch = HtmlEncodeAndReplaceLineBreaks(RuntimeInformation.ProcessArchitecture.ToString());
var currentAssembly = typeof(StartupExceptionPage).GetTypeInfo().Assembly;
var currentAssemblyVersion = currentAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
currentAssemblyVersion = HtmlEncodeAndReplaceLineBreaks(currentAssemblyVersion);
var osDescription = HtmlEncodeAndReplaceLineBreaks(RuntimeInformation.OSDescription);
return string.Format(CultureInfo.InvariantCulture, _errorFooterFormatString, runtimeDisplayName, runtimeArch, clrVersion,
currentAssemblyVersion, osDescription);
}
private static string HtmlEncodeAndReplaceLineBreaks(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
// Split on line breaks before passing it through the encoder.
// We use the static default encoder since we can't depend on DI in the error handling logic.
return string.Join("<br />" + Environment.NewLine,
input.Split(new[] { "\r\n" }, StringSplitOptions.None)
.SelectMany(s => s.Split(new[] { '\r', '\n' }, StringSplitOptions.None))
.Select(HtmlEncoder.Default.Encode));
}
private static void WriteException(Exception ex, StringBuilder builder, ref bool wasFailingCallSiteSourceWritten)
{
string inlineSourceDiv = null;
// First, build the stack trace
var firstStackFrame = true;
var stackTraceBuilder = new StringBuilder();
foreach (var frameInfo in StackTraceHelper.GetFrames(ex))
{
if (!firstStackFrame)
{
stackTraceBuilder.Append("<br />");
}
firstStackFrame = false;
var thisFrameLine = BuildLineForStackFrame(frameInfo);
stackTraceBuilder.AppendLine(thisFrameLine);
// Try to include the source code in the error page if we can.
if (!wasFailingCallSiteSourceWritten && inlineSourceDiv == null)
{
inlineSourceDiv = BuildCodeSnippetDiv(frameInfo);
if (inlineSourceDiv != null)
{
wasFailingCallSiteSourceWritten = true;
}
}
}
// Finally, build the rest of the <div>
builder.AppendFormat(CultureInfo.InvariantCulture, _errorExceptionFormatString,
HtmlEncodeAndReplaceLineBreaks(ex.GetType().FullName),
HtmlEncodeAndReplaceLineBreaks(ex.Message),
inlineSourceDiv,
stackTraceBuilder);
}
private static void WriteRawExceptionDetails(string linkText, string line, StringBuilder rawExceptionDetails)
{
rawExceptionDetails
.AppendLine("<div class=\"rawExceptionBlock\">")
.AppendFormat($" <div><a href=\"#\" onclick=\"javascript: showRawException(); return false;\">{linkText}</a></div>")
.AppendLine()
.AppendLine(" <div id=\"rawException\">")
.Append(" <pre>");
rawExceptionDetails.AppendLine(line);
rawExceptionDetails
.AppendLine("</pre>")
.AppendLine(" </div>")
.AppendLine("</div>");
}
private static void WriteMessage(string message, StringBuilder builder)
{
// Build the <div>
builder.AppendFormat(CultureInfo.InvariantCulture, _errorMessageFormatString,
HtmlEncodeAndReplaceLineBreaks(message));
}
private static IEnumerable<Exception> FlattenAndReverseExceptionTree(Exception ex)
{
// ReflectionTypeLoadException is special because the details are in
// the LoaderExceptions property
var typeLoadException = ex as ReflectionTypeLoadException;
if (typeLoadException != null)
{
var typeLoadExceptions = new List<Exception>();
foreach (var loadException in typeLoadException.LoaderExceptions)
{
typeLoadExceptions.AddRange(FlattenAndReverseExceptionTree(loadException));
}
typeLoadExceptions.Add(ex);
return typeLoadExceptions;
}
var list = new List<Exception>();
while (ex != null)
{
list.Add(ex);
ex = ex.InnerException;
}
list.Reverse();
return list;
}
}
}

View File

@ -24,17 +24,13 @@
"Microsoft.AspNetCore.Hosting.Server.Abstractions": "1.1.0-*",
"Microsoft.AspNetCore.Http": "1.1.0-*",
"Microsoft.AspNetCore.Http.Extensions": "1.1.0-*",
"Microsoft.Extensions.FileProviders.Physical": "1.1.0-*",
"Microsoft.Extensions.Options": "1.1.0-*",
"Microsoft.Extensions.Configuration": "1.1.0-*",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0-*",
"Microsoft.Extensions.DependencyInjection": "1.1.0-*",
"Microsoft.Extensions.FileProviders.Physical": "1.1.0-*",
"Microsoft.Extensions.Logging": "1.1.0-*",
"Microsoft.Extensions.Options": "1.1.0-*",
"Microsoft.Extensions.PlatformAbstractions": "1.1.0-*",
"Microsoft.Extensions.RazorViews.Sources": {
"type": "build",
"version": "1.1.0-*"
},
"Microsoft.Extensions.RuntimeEnvironment.Sources": {
"type": "build",
"version": "1.1.0-*"
@ -72,8 +68,5 @@
"System.Reflection.Extensions": "4.0.1-*"
}
}
},
"tools": {
"RazorPageGenerator": "1.1.0-*"
}
}

View File

@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Hosting
using (host)
{
host.Start();
await AssertResponseContains(server.RequestDelegate, "Message from the LoaderException</div>");
await AssertResponseContains(server.RequestDelegate, "Message from the LoaderException</span>");
}
}
@ -481,20 +481,20 @@ namespace Microsoft.AspNetCore.Hosting
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests", hostingEnv.ApplicationName);
}
[Fact]
public void Configure_SupportsStaticMethodDelegate()
{
var host = new WebHostBuilder()
.UseServer(new TestServer())
.Configure(StaticConfigureMethod)
.Configure(StaticConfigureMethod)
.Build();
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests", hostingEnv.ApplicationName);
}
private static void StaticConfigureMethod(IApplicationBuilder app)
private static void StaticConfigureMethod(IApplicationBuilder app)
{ }
private IWebHostBuilder CreateWebHostBuilder()