From 2c001e331f9f663d4c7755ef62f2d71a81dd9e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20H=C3=A5kansson?= Date: Thu, 26 May 2016 09:39:23 +0200 Subject: [PATCH] Updated the look and feel of the error pages --- .../DotLiquidViewEngine.cs | 26 +- .../Resources/500.liquid | 235 +++++++--------- .../NancyRazorErrorView.cs | 2 +- .../RazorViewEngine.cs | 10 +- .../Resources/CompilationError.html | 266 ++++++++---------- .../ErrorHandling/DefaultStatusCodeHandler.cs | 8 +- src/Nancy/ErrorHandling/Resources/404.html | 165 ++++++----- src/Nancy/ErrorHandling/Resources/500.html | 243 +++++++--------- .../ViewEngines/ViewNotFoundException.cs | 17 +- .../Tests/ContentNegotiationFixture.cs | 4 +- 10 files changed, 459 insertions(+), 517 deletions(-) diff --git a/src/Nancy.ViewEngines.DotLiquid/DotLiquidViewEngine.cs b/src/Nancy.ViewEngines.DotLiquid/DotLiquidViewEngine.cs index 17b7680dc9..9a67523e82 100644 --- a/src/Nancy.ViewEngines.DotLiquid/DotLiquidViewEngine.cs +++ b/src/Nancy.ViewEngines.DotLiquid/DotLiquidViewEngine.cs @@ -51,7 +51,7 @@ public IEnumerable Extensions } /// - /// Initialise the view engine (if necessary) + /// Initialize the view engine (if necessary) /// /// Startup context public void Initialize(ViewEngineStartupContext viewEngineStartupContext) @@ -100,13 +100,15 @@ public Response RenderView(ViewLocationResult viewLocationResult, dynamic model, status = HttpStatusCode.InternalServerError; // Build the error message - String errorMessage = String.Format("Syntax error in liquid view '{0}':\r\n\r\n{1}", - String.Format("{0}/{1}.{2}", viewLocationResult.Location, viewLocationResult.Name, viewLocationResult.Extension), + var errorMessage = string.Format("Syntax error in liquid view '{0}'

{1}", + string.Format("{0}/{1}.{2}", viewLocationResult.Location, viewLocationResult.Name, viewLocationResult.Extension), syntaxException.Message); // Create the error model with a Nancy DynamicDictionary because i can ;) - DynamicDictionary errorModel = new DynamicDictionary(); - errorModel.Add(new KeyValuePair("ErrorMessage", errorMessage)); + var errorModel = new DynamicDictionary + { + new KeyValuePair("ErrorMessage", errorMessage) + }; // Hash up the Error model so DotLiquid will understand it hashedModel = @@ -116,18 +118,20 @@ public Response RenderView(ViewLocationResult viewLocationResult, dynamic model, }); // Grab the error HTML from the embedded resource and build up the DotLiquid template. - String errorHtml = LoadResource(@"500.liquid"); + var errorHtml = LoadResource(@"500.liquid"); parsed = Template.Parse(errorHtml); } catch (Exception ex) { status = HttpStatusCode.InternalServerError; // Build the error message - String errorMessage = String.Format("Error: {0}", ex.Message); + var errorMessage = string.Format("Error: {0}", ex.Message); // Create the error model with a Nancy DynamicDictionary because i can ;) - DynamicDictionary errorModel = new DynamicDictionary(); - errorModel.Add(new KeyValuePair("ErrorMessage", errorMessage)); + var errorModel = new DynamicDictionary + { + new KeyValuePair("ErrorMessage", errorMessage) + }; // Hash up the Error model so DotLiquid will understand it hashedModel = @@ -137,7 +141,7 @@ public Response RenderView(ViewLocationResult viewLocationResult, dynamic model, }); // Grab the error HTML from the embedded resource - String errorHtml = LoadResource(@"500.liquid"); + var errorHtml = LoadResource(@"500.liquid"); parsed = Template.Parse(errorHtml); } @@ -154,7 +158,7 @@ public Response RenderView(ViewLocationResult viewLocationResult, dynamic model, private static string LoadResource(string filename) { - var resourceStream = typeof(DotLiquidViewEngine).Assembly.GetManifestResourceStream(String.Format("Nancy.ViewEngines.DotLiquid.Resources.{0}", filename)); + var resourceStream = typeof(DotLiquidViewEngine).Assembly.GetManifestResourceStream(string.Format("Nancy.ViewEngines.DotLiquid.Resources.{0}", filename)); if (resourceStream == null) { diff --git a/src/Nancy.ViewEngines.DotLiquid/Resources/500.liquid b/src/Nancy.ViewEngines.DotLiquid/Resources/500.liquid index c9602a9b13..1f648417c1 100644 --- a/src/Nancy.ViewEngines.DotLiquid/Resources/500.liquid +++ b/src/Nancy.ViewEngines.DotLiquid/Resources/500.liquid @@ -1,136 +1,117 @@ - + - 500 - + body { + background: linear-gradient(-180deg, #000000, #434343) #000 fixed; + color: #333; + display: table; + font-family: sans-serif; + height: 100%; + margin: 0; + width: 100%; + } - - - + img { + float: left; + margin-right: 40px; + } + + h1 { + font-family: sans-serif; + margin-bottom: 5px; + } + + h1, h2 { + color: #333; + } + + .container { + display: table-cell; + vertical-align: middle; + padding: 20px; + } + + .box { + background-color: #f6f8f8; + border-radius: 4px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .3); + margin: 0 auto; + max-width: 960px; + padding: 25px; + } + + .header { + border-bottom: #ccc 1px dotted; + padding-bottom: 20px; + overflow: hidden; + vertical-align: middle; + } + + .header div { + float: left; + } + + footer { + color: #f6f8f8; + font-size: 12px; + margin-top: 10px; + text-align: center; + } + + pre { + background-image: -webkit-repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + background-image: -moz-repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + background-image: -ms-repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + background-image: -o-repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + background-image: repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + color: #555; + display:block; + font: normal 14px/22px Monaco, Monospace; + margin-bottom: 0; + overflow: auto; + padding: 0 1em; + white-space: pre-wrap; + } + + footer a, + footer a:hover, + footer a:visited { + color: #6daf00; + } + + a, + a:hover, + a:visited { + color: #e74c3c; + } + -
-
-
- -
-
-

500 - InternalServerError

-

Something went horribly, horribly wrong while servicing your request.

-

We're sorry :-(

-
-
-
-
-

Graphics courtesy of the awesome Matthew Inman

+
+
+
+ Error Image +
+

500 - Internal Server Error

+

Something went horribly, horribly wrong while servicing your request.

+

We're sorry ☹

+
+
+ +
+

Error Details

+
{{ Model.ErrorMessage }}
+
+
+
- -
-
-
-

Error Details

-
{{ Model.ErrorMessage }}
-
- diff --git a/src/Nancy.ViewEngines.Razor/NancyRazorErrorView.cs b/src/Nancy.ViewEngines.Razor/NancyRazorErrorView.cs index 938c1ce361..1b4e132916 100644 --- a/src/Nancy.ViewEngines.Razor/NancyRazorErrorView.cs +++ b/src/Nancy.ViewEngines.Razor/NancyRazorErrorView.cs @@ -10,7 +10,7 @@ public class NancyRazorErrorView : NancyRazorViewBase { private readonly TraceConfiguration traceConfiguration; - private const string DisplayErrorTracesFalseMessage = "Error details are currently disabled.
To enable it, please set TraceConfiguration.DisplayErrorTraces to true.
For example by overriding your Bootstrapper's Configure method and calling environment.Tracing(enabled: false, displayErrorTraces: true);."; + private const string DisplayErrorTracesFalseMessage = "Error details are currently disabled.
To enable it, please set TraceConfiguration.DisplayErrorTraces to true.
For example by overriding your Bootstrapper's Configure method and calling
environment.Tracing(enabled: false, displayErrorTraces: true);."; private static string template; diff --git a/src/Nancy.ViewEngines.Razor/RazorViewEngine.cs b/src/Nancy.ViewEngines.Razor/RazorViewEngine.cs index 87b9aba181..26bebc75bc 100644 --- a/src/Nancy.ViewEngines.Razor/RazorViewEngine.cs +++ b/src/Nancy.ViewEngines.Razor/RazorViewEngine.cs @@ -253,11 +253,11 @@ private static string BuildErrorMessage(EmitResult result, ViewLocationResult vi var lineNumber = 1; var errorDetails = string.Format( - "Error compiling template: {0}

Errors:
{1}

Details:
{2}

Compilation Source:
{3}
", + "Template: {0}

Errors:
    {1}
Details:
{2}

Compilation Source:
{3}
", fullTemplateName, errorMessages, templateLines.Aggregate((s1, s2) => s1 + "
" + s2), - compilationSource.Aggregate((s1, s2) => s1 + "
Line " + lineNumber++ + ":\t" + s2)); + compilationSource.Aggregate((s1, s2) => s1 + "
Line " + lineNumber++ + ":\t" + s2.Replace("<", ">").Replace(">", "<"))); return errorDetails; } @@ -333,10 +333,10 @@ private static string BuildErrorMessages(IEnumerable errors, IList 0) { - templateLines[lineNumber - 1] = string.Format("{1}", lineNumber, templateLines[lineNumber - 1]); + templateLines[lineNumber - 1] = string.Format("{1}", lineNumber, templateLines[lineNumber - 1]); return string.Format( - "[{0}] Line: {1} Column: {2} - {3} (show)", + "
  • [{0}] Line: {1} Column: {2} - {3} (show)
  • ", error.Id, lineNumber, error.Location.GetLineSpan().StartLinePosition.Character, @@ -348,7 +348,7 @@ private static string BuildErrorMessages(IEnumerable errors, IList x != null).ToArray(); return (messages.Any()) - ? messages.Aggregate((s1, s2) => s1 + "
    " + s2) + ? string.Join(string.Empty, messages) : string.Empty; } diff --git a/src/Nancy.ViewEngines.Razor/Resources/CompilationError.html b/src/Nancy.ViewEngines.Razor/Resources/CompilationError.html index 6939681b15..a5e8c73024 100644 --- a/src/Nancy.ViewEngines.Razor/Resources/CompilationError.html +++ b/src/Nancy.ViewEngines.Razor/Resources/CompilationError.html @@ -1,149 +1,131 @@ - + - Razor Compilation Error - - - - - + Nancy - Razor Compilation Error + + -
    -
    -
    - -
    -
    -

    Razor Compilation Error

    -

    We tried, we really did, but we just can't compile your view.

    -

    We're sorry :-(

    -
    -
    -
    -
    -

    Graphics courtesy of the awesome Matthew Inman

    +
    +
    +
    + Error Image +
    +

    Razor Compilation Error

    +

    We tried, we really did, but we just can't compile your view.

    +

    We're sorry ☹

    +
    +
    + +
    +

    Error Details

    + [DETAILS] +
    +
    +
    - -
    -
    -
    -

    Error Details

    -
    [DETAILS]
    -
    - diff --git a/src/Nancy/ErrorHandling/DefaultStatusCodeHandler.cs b/src/Nancy/ErrorHandling/DefaultStatusCodeHandler.cs index 22a5059ff4..8d2b500f4e 100644 --- a/src/Nancy/ErrorHandling/DefaultStatusCodeHandler.cs +++ b/src/Nancy/ErrorHandling/DefaultStatusCodeHandler.cs @@ -15,7 +15,7 @@ namespace Nancy.ErrorHandling /// public class DefaultStatusCodeHandler : IStatusCodeHandler { - private const string DisplayErrorTracesFalseMessage = "Error details are currently disabled.
    To enable it, please set TraceConfiguration.DisplayErrorTraces to true.
    For example by overriding your Bootstrapper's Configure method and calling environment.Tracing(enabled: false, displayErrorTraces: true);."; + private const string DisplayErrorTracesFalseMessage = "Error details are currently disabled.
    To enable it, please set TraceConfiguration.DisplayErrorTraces to true.
    For example by overriding your Bootstrapper's Configure method and calling
    environment.Tracing(enabled: false, displayErrorTraces: true);."; private readonly IDictionary errorMessages; private readonly IDictionary errorPages; @@ -86,7 +86,11 @@ public void Handle(HttpStatusCode statusCode, NancyContext context) // from swapping a view model with a `DefaultStatusCodeHandlerResult` context.NegotiationContext = new NegotiationContext(); - var result = new DefaultStatusCodeHandlerResult(statusCode, this.errorMessages[statusCode], !this.configuration.DisplayErrorTraces ? DisplayErrorTracesFalseMessage : context.GetExceptionDetails()); + var details = !this.configuration.DisplayErrorTraces + ? DisplayErrorTracesFalseMessage + : string.Concat("
    ", context.GetExceptionDetails().Replace("<", ">").Replace(">", "<"), "
    "); + + var result = new DefaultStatusCodeHandlerResult(statusCode, this.errorMessages[statusCode], details); try { context.Response = this.responseNegotiator.NegotiateResponse(result, context); diff --git a/src/Nancy/ErrorHandling/Resources/404.html b/src/Nancy/ErrorHandling/Resources/404.html index a2cdc0107b..a0e55a902d 100644 --- a/src/Nancy/ErrorHandling/Resources/404.html +++ b/src/Nancy/ErrorHandling/Resources/404.html @@ -1,93 +1,90 @@ - + - 404 - + body { + background: linear-gradient(-180deg, #000000, #434343) #000 fixed; + color: #333; + display: table; + font-family: sans-serif; + height: 100%; + margin: 0; + width: 100%; + } - + img { + float: left; + margin-right: 40px; + } + h1 { + font-family: sans-serif; + margin-bottom: 5px; + } + + h1, h2 { + color: #333; + } + + .container { + display: table-cell; + vertical-align: middle; + padding: 20px; + } + + .box { + background-color: #f6f8f8; + border-radius: 4px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .3); + margin: 0 auto; + max-width: 960px; + padding: 25px; + } + + .header { + overflow: hidden; + vertical-align: middle; + } + + .header div { + float: left; + } + + footer { + color: #f6f8f8; + font-size: 12px; + margin-top: 10px; + text-align: center; + } + + footer a, + footer a:hover, + footer a:visited { + color: #6daf00; + } + -
    -
    -
    - -
    -
    -

    404 - NotFound

    -

    The resource you have requested cannot be found.

    -

    We're sorry :-(

    -
    -
    -
    -
    -

    Graphics courtesy of the awesome Matthew Inman

    +
    +
    +
    + Error Image +
    +

    404 - Not Found

    +

    The resource you have requested cannot be found.

    +

    We're sorry ☹

    +
    +
    +
    + +
    -
    - \ No newline at end of file + diff --git a/src/Nancy/ErrorHandling/Resources/500.html b/src/Nancy/ErrorHandling/Resources/500.html index e2b1e66f1a..7fdcb16097 100644 --- a/src/Nancy/ErrorHandling/Resources/500.html +++ b/src/Nancy/ErrorHandling/Resources/500.html @@ -1,144 +1,117 @@ - + - 500 - + body { + background: linear-gradient(-180deg, #000000, #434343) #000 fixed; + color: #333; + display: table; + font-family: sans-serif; + height: 100%; + margin: 0; + width: 100%; + } - - - + img { + float: left; + margin-right: 40px; + } + + h1 { + font-family: sans-serif; + margin-bottom: 5px; + } + + h1, h2 { + color: #333; + } + + .container { + display: table-cell; + vertical-align: middle; + padding: 20px; + } + + .box { + background-color: #f6f8f8; + border-radius: 4px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .3); + margin: 0 auto; + max-width: 960px; + padding: 25px; + } + + .header { + border-bottom: #ccc 1px dotted; + padding-bottom: 20px; + overflow: hidden; + vertical-align: middle; + } + + .header div { + float: left; + } + + footer { + color: #f6f8f8; + font-size: 12px; + margin-top: 10px; + text-align: center; + } + + pre { + background-image: -webkit-repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + background-image: -moz-repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + background-image: -ms-repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + background-image: -o-repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + background-image: repeating-linear-gradient(top, #efefef 0px, #efefef 22px, #e0e0e0 22px, #e0e0e0 44px); + color: #555; + display:block; + font: normal 14px/22px Monaco, Monospace; + margin-bottom: 0; + overflow: auto; + padding: 0 1em; + white-space: pre-wrap; + } + + footer a, + footer a:hover, + footer a:visited { + color: #6daf00; + } + + a, + a:hover, + a:visited { + color: #e74c3c; + } + -
    -
    -
    - -
    -
    -

    500 - InternalServerError

    -

    Something went horribly, horribly wrong while servicing your request.

    -

    We're sorry :-(

    -
    -
    -
    -
    -

    Graphics courtesy of the awesome Matthew Inman

    +
    +
    +
    + Error Image +
    +

    500 - Internal Server Error

    +

    Something went horribly, horribly wrong while servicing your request.

    +

    We're sorry ☹

    +
    +
    + +
    +

    Error Details

    + [DETAILS] +
    +
    +
    - -
    -
    -
    -

    Error Details

    -
    [DETAILS]
    -
    - diff --git a/src/Nancy/ViewEngines/ViewNotFoundException.cs b/src/Nancy/ViewEngines/ViewNotFoundException.cs index 00322f3498..6522f959c3 100644 --- a/src/Nancy/ViewEngines/ViewNotFoundException.cs +++ b/src/Nancy/ViewEngines/ViewNotFoundException.cs @@ -1,6 +1,7 @@ namespace Nancy.ViewEngines { using System; + using System.Linq; /// /// Exception that is thrown when a view could not be located. @@ -29,14 +30,14 @@ public ViewNotFoundException(string viewName, string[] availableViewEngineExtens this.AvailableViewEngineExtensions = availableViewEngineExtensions; this.InspectedLocations = inspectedLocations; - this.message = String.Format( - "Unable to locate view '{0}'{4}Currently available view engine extensions: {1}{4}Locations inspected: {2}{4}Root path: {3}{4}" + - "If you were expecting raw data back, make sure you set the 'Accept'-header of the request to correct format, for example 'application/json'", - this.ViewName, - string.Join(",", this.AvailableViewEngineExtensions), - string.Join(",", this.InspectedLocations), - this.rootPathProvider.GetRootPath(), - Environment.NewLine); + this.message = string.Format( + "{4}Unable to locate requested view{4}{4} \u2022 Name: {0}{4} \u2022 Root path: {3}{4} \u2022 Supported extensions: {4}{1} \u2022 Inspected locations: {4}{2}{4}" + + "If you were expecting raw data back, make sure you set the 'Accept'-header of the request to correct format, for example 'application/json'{4}", + this.ViewName, + string.Join(string.Empty, this.AvailableViewEngineExtensions.Select(x => string.Concat(" - ", x, Environment.NewLine))), + string.Join(string.Empty, this.InspectedLocations.Select(x => string.Concat(" - ", x, Environment.NewLine))), + this.rootPathProvider.GetRootPath(), + Environment.NewLine); } /// diff --git a/test/Nancy.Tests.Functional/Tests/ContentNegotiationFixture.cs b/test/Nancy.Tests.Functional/Tests/ContentNegotiationFixture.cs index 52d1d1b886..18260e2fbe 100644 --- a/test/Nancy.Tests.Functional/Tests/ContentNegotiationFixture.cs +++ b/test/Nancy.Tests.Functional/Tests/ContentNegotiationFixture.cs @@ -586,7 +586,7 @@ public async Task Should_throw_exception_if_view_location_fails() // Then Assert.NotNull(result); - Assert.Contains("Unable to locate view", result.ToString()); + Assert.Contains("Unable to locate requested view", result.ToString()); } [Fact] @@ -653,7 +653,7 @@ public async Task Should_not_try_and_serve_view_with_invalid_name() var result = await RecordAsync.Exception(() => browser.Get("/invalid-view-name")); // Then - Assert.True(result.ToString().Contains("Unable to locate view")); + Assert.True(result.ToString().Contains("Unable to locate requested view")); } [Fact]