Remove existing implementation of StartupExceptionPage and use the one in Common
[Fixes #831] Fix startup exception page to handle flattened exceptions
This commit is contained in:
parent
e927dd6924
commit
83e0d4798e
|
|
@ -5,16 +5,20 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting.Builder;
|
using Microsoft.AspNetCore.Hosting.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting.Server;
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Views;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.StackTrace.Sources;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Hosting.Internal
|
namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -168,15 +172,44 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
// Generate an HTML error page.
|
// Generate an HTML error page.
|
||||||
var hostingEnv = _applicationServices.GetRequiredService<IHostingEnvironment>();
|
var hostingEnv = _applicationServices.GetRequiredService<IHostingEnvironment>();
|
||||||
var showDetailedErrors = hostingEnv.IsDevelopment() || _options.DetailedErrors;
|
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 =>
|
return context =>
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = 500;
|
context.Response.StatusCode = 500;
|
||||||
context.Response.Headers["Cache-Control"] = "private, max-age=0";
|
context.Response.Headers["Cache-Control"] = "no-cache";
|
||||||
context.Response.ContentType = "text/html; charset=utf-8";
|
return errorPage.ExecuteAsync(context);
|
||||||
context.Response.ContentLength = errorBytes.Length;
|
|
||||||
return context.Response.Body.WriteAsync(errorBytes, 0, errorBytes.Length);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
// <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
<?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>
|
||||||
1055
src/Microsoft.AspNetCore.Hosting/Startup/ExceptionPage/Views/ErrorPage.Designer.cs
generated
Normal file
1055
src/Microsoft.AspNetCore.Hosting/Startup/ExceptionPage/Views/ErrorPage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,162 @@
|
||||||
|
@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) | Microsoft.AspNetCore.Hosting version @Model.CurrentAssemblyVesion | @Model.OperatingSystemDescription | <a href="http://go.microsoft.com/fwlink/?LinkId=517394">Need help?</a>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
//<!--
|
||||||
|
<%$ include: ErrorPage.js %>
|
||||||
|
//-->
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
(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);
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,329 +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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -24,13 +24,17 @@
|
||||||
"Microsoft.AspNetCore.Hosting.Server.Abstractions": "1.1.0-*",
|
"Microsoft.AspNetCore.Hosting.Server.Abstractions": "1.1.0-*",
|
||||||
"Microsoft.AspNetCore.Http": "1.1.0-*",
|
"Microsoft.AspNetCore.Http": "1.1.0-*",
|
||||||
"Microsoft.AspNetCore.Http.Extensions": "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": "1.1.0-*",
|
||||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0-*",
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0-*",
|
||||||
"Microsoft.Extensions.DependencyInjection": "1.1.0-*",
|
"Microsoft.Extensions.DependencyInjection": "1.1.0-*",
|
||||||
|
"Microsoft.Extensions.FileProviders.Physical": "1.1.0-*",
|
||||||
"Microsoft.Extensions.Logging": "1.1.0-*",
|
"Microsoft.Extensions.Logging": "1.1.0-*",
|
||||||
|
"Microsoft.Extensions.Options": "1.1.0-*",
|
||||||
"Microsoft.Extensions.PlatformAbstractions": "1.1.0-*",
|
"Microsoft.Extensions.PlatformAbstractions": "1.1.0-*",
|
||||||
|
"Microsoft.Extensions.RazorViews.Sources": {
|
||||||
|
"type": "build",
|
||||||
|
"version": "1.1.0-*"
|
||||||
|
},
|
||||||
"Microsoft.Extensions.RuntimeEnvironment.Sources": {
|
"Microsoft.Extensions.RuntimeEnvironment.Sources": {
|
||||||
"type": "build",
|
"type": "build",
|
||||||
"version": "1.1.0-*"
|
"version": "1.1.0-*"
|
||||||
|
|
@ -68,5 +72,8 @@
|
||||||
"System.Reflection.Extensions": "4.0.1-*"
|
"System.Reflection.Extensions": "4.0.1-*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"RazorPageGenerator": "1.1.0-*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
using (host)
|
using (host)
|
||||||
{
|
{
|
||||||
host.Start();
|
host.Start();
|
||||||
await AssertResponseContains(server.RequestDelegate, "Message from the LoaderException</span>");
|
await AssertResponseContains(server.RequestDelegate, "Message from the LoaderException</div>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,20 +481,20 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
|
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
|
||||||
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests", hostingEnv.ApplicationName);
|
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests", hostingEnv.ApplicationName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Configure_SupportsStaticMethodDelegate()
|
public void Configure_SupportsStaticMethodDelegate()
|
||||||
{
|
{
|
||||||
var host = new WebHostBuilder()
|
var host = new WebHostBuilder()
|
||||||
.UseServer(new TestServer())
|
.UseServer(new TestServer())
|
||||||
.Configure(StaticConfigureMethod)
|
.Configure(StaticConfigureMethod)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
|
var hostingEnv = host.Services.GetService<IHostingEnvironment>();
|
||||||
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests", hostingEnv.ApplicationName);
|
Assert.Equal("Microsoft.AspNetCore.Hosting.Tests", hostingEnv.ApplicationName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StaticConfigureMethod(IApplicationBuilder app)
|
private static void StaticConfigureMethod(IApplicationBuilder app)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
private IWebHostBuilder CreateWebHostBuilder()
|
private IWebHostBuilder CreateWebHostBuilder()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue