Adding ability to turn off the status code pages

Added a feature named IStatusCodeFeature with an enabled property. MVC or any framework can get this feature and turn it off if
it does not want status code pages logic to take over and send HTML response.

Also refactored the sample flow to make it easier to understand.
This commit is contained in:
Praburaj 2015-02-11 10:13:16 -08:00
parent 31afaca7e3
commit f243244972
14 changed files with 141 additions and 45 deletions

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22228.1
VisualStudioVersion = 14.0.22530.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{509A6F36-AD80-4A18-B5B1-717D38DFF29D}"
EndProject
@ -38,6 +38,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Diagnostic
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "StatusCodePagesSample", "samples\StatusCodePagesSample\StatusCodePagesSample.kproj", "{CC1F5841-FE10-4DDB-8477-C4DE92BA759F}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Diagnostics.Interfaces", "src\Microsoft.AspNet.Diagnostics.Interfaces\Microsoft.AspNet.Diagnostics.Interfaces.kproj", "{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -178,6 +180,18 @@ Global
{CC1F5841-FE10-4DDB-8477-C4DE92BA759F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CC1F5841-FE10-4DDB-8477-C4DE92BA759F}.Release|x86.ActiveCfg = Release|Any CPU
{CC1F5841-FE10-4DDB-8477-C4DE92BA759F}.Release|x86.Build.0 = Release|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Debug|x86.ActiveCfg = Debug|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Debug|x86.Build.0 = Debug|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Release|Any CPU.Build.0 = Release|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Release|x86.ActiveCfg = Release|Any CPU
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -195,5 +209,6 @@ Global
{2F9B479D-8247-4210-804B-78E6DD5C3E98} = {2AF90579-B118-4583-AE88-672EFACB5BC4}
{624B0019-956A-4157-B008-270C5B229553} = {509A6F36-AD80-4A18-B5B1-717D38DFF29D}
{CC1F5841-FE10-4DDB-8477-C4DE92BA759F} = {ACAA0157-A8C4-4152-93DE-90CCDF304087}
{83FFB65A-97B1-45AA-BCB8-3F43966BC8A3} = {509A6F36-AD80-4A18-B5B1-717D38DFF29D}
EndGlobalSection
EndGlobal

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
@ -25,54 +26,75 @@ namespace StatusCodePagesSample
// app.UseStatusCodePagesWithReExecute("/errors/{0}");
// "/[?statuscode=400]"
app.Use((context, next) =>
app.Use(async (context, next) =>
{
if (context.Request.Path.HasValue && !context.Request.Path.Equals(new PathString("/")))
{
return next();
}
// Check for ?statuscode=400
var requestedStatusCode = context.Request.Query["statuscode"];
if (!string.IsNullOrEmpty(requestedStatusCode))
{
context.Response.StatusCode = int.Parse(requestedStatusCode);
return Task.FromResult(0);
// To turn off the StatusCode feature - For example the below code turns off the StatusCode middleware
// if the query contains a disableStatusCodePages=true parameter.
var disableStatusCodePages = context.Request.Query["disableStatusCodePages"];
if (disableStatusCodePages == "true")
{
var statusCodePagesFeature = context.GetFeature<IStatusCodePagesFeature>();
if (statusCodePagesFeature != null)
{
statusCodePagesFeature.Enabled = false;
}
}
await Task.FromResult(0);
}
var builder = new StringBuilder();
builder.AppendLine("<html><body>");
builder.AppendLine("<a href=\"" +
WebUtility.HtmlEncode(context.Request.PathBase.ToString()) + "/missingpage/\">" +
WebUtility.HtmlEncode(context.Request.PathBase.ToString()) + "/missingpage/</a><br>");
for (int statusCode = 400; statusCode < 600; statusCode++)
else
{
builder.AppendLine("<a href=\"?statuscode=" + statusCode + "\">" + statusCode + "</a><br>");
await next();
}
builder.AppendLine("</body></html>");
return context.Response.SendAsync(builder.ToString(), "text/html");
});
// "/errors/400"
app.Use((context, next) =>
app.Map("/errors", error =>
{
PathString remainder;
if (context.Request.Path.StartsWithSegments(new PathString("/errors"), out remainder))
error.Run(async context =>
{
var builder = new StringBuilder();
builder.AppendLine("<html><body>");
builder.AppendLine("An error occurred, Status Code: " + WebUtility.HtmlEncode(remainder.ToString().Substring(1)) + "<br>");
builder.AppendLine("An error occurred, Status Code: " + WebUtility.HtmlEncode(context.Request.Path.ToString().Substring(1)) + "<br>");
var referrer = context.Request.Headers["referer"];
if (!string.IsNullOrEmpty(referrer))
{
builder.AppendLine("<a href=\"" + WebUtility.HtmlEncode(referrer) + "\">Retry " + WebUtility.HtmlEncode(referrer) + "</a><br>");
}
builder.AppendLine("</body></html>");
return context.Response.SendAsync(builder.ToString(), "text/html");
await context.Response.SendAsync(builder.ToString(), "text/html");
});
});
app.Run(async context =>
{
// Generates the HTML with all status codes.
var builder = new StringBuilder();
builder.AppendLine("<html><body>");
builder.AppendLine("<a href=\"" +
WebUtility.HtmlEncode(context.Request.PathBase.ToString()) + "/missingpage/\">" +
WebUtility.HtmlEncode(context.Request.PathBase.ToString()) + "/missingpage/</a><br>");
var space = string.Concat(Enumerable.Repeat("&nbsp;", 12));
builder.AppendFormat("<br><b>{0}{1}{2}</b><br>", "Status Code", space, "Status Code Pages");
for (int statusCode = 400; statusCode < 600; statusCode++)
{
builder.AppendFormat("{0}{1}{2}{3}<br>",
statusCode,
space + space,
string.Format("<a href=\"?statuscode={0}\">[Enabled]</a>{1}", statusCode, space),
string.Format("<a href=\"?statuscode={0}&disableStatusCodePages=true\">[Disabled]</a>{1}", statusCode, space));
}
return next();
builder.AppendLine("</body></html>");
await context.Response.SendAsync(builder.ToString(), "text/html");
});
}
}
}
}

View File

@ -2,8 +2,7 @@
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
"Microsoft.Framework.Logging.Interfaces": { "version": "1.0.0-*", "type": "build" },
"Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" }
"Microsoft.Framework.Logging.Interfaces": "1.0.0-*"
},
"frameworks": {

View File

@ -2,14 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Diagnostics
{
/// <summary>
/// Specifies the contract for an exception representing compilation failure.
/// </summary>
[AssemblyNeutral]
public interface ICompilationException
{
/// <summary>

View File

@ -2,14 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Diagnostics
{
/// <summary>
/// Specifies the contract for a file that fails compilation.
/// </summary>
[AssemblyNeutral]
public interface ICompilationFailure
{
/// <summary>

View File

@ -1,15 +1,12 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Diagnostics
{
/// <summary>
/// Specifies the contract for diagnostic messages produced as result of compiling an instance
/// of <see cref="ICompilationFailure"/>.
/// </summary>
[AssemblyNeutral]
public interface ICompilationMessage
{
/// <summary>

View File

@ -2,11 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Diagnostics
{
[AssemblyNeutral]
public interface IErrorHandlerFeature
{
Exception Error { get; }

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Diagnostics
{
/// <summary>
/// Represents the Status code pages feature.
/// </summary>
public interface IStatusCodePagesFeature
{
/// <summary>
/// Indicates if the status code middleware will handle responses.
/// </summary>
bool Enabled { get; set; }
}
}

View File

@ -1,11 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Diagnostics
{
[AssemblyNeutral]
public interface IStatusCodeReExecuteFeature
{
string OriginalPathBase { get; set; }

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>83ffb65a-97b1-45aa-bcb8-3f43966bc8a3</ProjectGuid>
<RootNamespace>Microsoft.AspNet.Diagnostics.Interfaces</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,13 @@
{
"version": "1.0.0-*",
"description": "ASP.NET 5 diagnostics middleware interfaces.",
"compilationOptions": { "warningsAsErrors": true },
"frameworks": {
"aspnet50": { },
"aspnetcore50": {
"dependencies": {
"System.Runtime": "4.0.20-beta-*"
}
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Diagnostics
{
/// <summary>
/// Represents the Status code pages feature.
/// </summary>
public class StatusCodePagesFeature : IStatusCodePagesFeature
{
public bool Enabled { get; set; } = true;
}
}

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Diagnostics
private readonly RequestDelegate _next;
private readonly StatusCodePagesOptions _options;
public StatusCodePagesMiddleware(RequestDelegate next, [NotNull]StatusCodePagesOptions options)
public StatusCodePagesMiddleware(RequestDelegate next, StatusCodePagesOptions options)
{
_next = next;
_options = options;
@ -25,8 +25,18 @@ namespace Microsoft.AspNet.Diagnostics
public async Task Invoke(HttpContext context)
{
var statusCodeFeature = new StatusCodePagesFeature();
context.SetFeature<IStatusCodePagesFeature>(statusCodeFeature);
await _next(context);
if (!statusCodeFeature.Enabled)
{
// Check if the feature is still available because other middleware (such as a web API written in MVC) could
// have disabled the feature to prevent HTML status code responses from showing up to an API client.
return;
}
// Do nothing if a response body has already been provided.
if (context.Response.HeadersSent
|| context.Response.StatusCode < 400

View File

@ -2,16 +2,16 @@
"version": "1.0.0-*",
"description": "ASP.NET 5 Middleware for error handling, error pages, and diagnostics information.",
"dependencies": {
"Microsoft.AspNet.Diagnostics.Interfaces": "1.0.0-*",
"Microsoft.AspNet.RequestContainer": "1.0.0-*",
"Microsoft.AspNet.WebUtilities": "1.0.0-*",
"Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" }
"Microsoft.AspNet.WebUtilities": "1.0.0-*"
},
"frameworks": {
"aspnet50": { },
"aspnet50": {},
"aspnetcore50": {
"dependencies": {
"System.Reflection.Extensions": "4.0.0-beta-*"
}
}
}
}
}