[Fixes #40] Suppress caching for ErrorHandler
This commit is contained in:
parent
8c8490fab4
commit
236d4009c3
|
|
@ -14,6 +14,7 @@ namespace Microsoft.AspNet.Diagnostics
|
|||
private readonly RequestDelegate _next;
|
||||
private readonly ErrorHandlerOptions _options;
|
||||
private readonly ILogger _logger;
|
||||
private readonly Func<object, Task> _clearCacheHeadersDelegate;
|
||||
|
||||
public ErrorHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, ErrorHandlerOptions options)
|
||||
{
|
||||
|
|
@ -24,6 +25,7 @@ namespace Microsoft.AspNet.Diagnostics
|
|||
{
|
||||
_options.ErrorHandler = _next;
|
||||
}
|
||||
_clearCacheHeadersDelegate = ClearCacheHeaders;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
|
|
@ -56,6 +58,8 @@ namespace Microsoft.AspNet.Diagnostics
|
|||
context.SetFeature<IErrorHandlerFeature>(errorHandlerFeature);
|
||||
context.Response.StatusCode = 500;
|
||||
context.Response.Headers.Clear();
|
||||
context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response);
|
||||
|
||||
// TODO: Try clearing any buffered data. The buffering feature/middleware has not been designed yet.
|
||||
await _options.ErrorHandler(context);
|
||||
// TODO: Optional re-throw? We'll re-throw the original exception by default if the error handler throws.
|
||||
|
|
@ -70,9 +74,18 @@ namespace Microsoft.AspNet.Diagnostics
|
|||
{
|
||||
context.Request.Path = originalPath;
|
||||
}
|
||||
|
||||
throw; // Re-throw the original if we couldn't handle it
|
||||
}
|
||||
}
|
||||
|
||||
private Task ClearCacheHeaders(object state)
|
||||
{
|
||||
var response = (HttpResponse)state;
|
||||
response.Headers.Set("Cache-Control", "no-cache");
|
||||
response.Headers.Set("Pragma", "no-cache");
|
||||
response.Headers.Set("Expires", "-1");
|
||||
response.Headers.Remove("ETag");
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
// 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.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Diagnostics
|
||||
{
|
||||
public class ErrorHandlerTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(HttpStatusCode.NotFound)]
|
||||
[InlineData(HttpStatusCode.BadRequest)]
|
||||
[InlineData(HttpStatusCode.InternalServerError)]
|
||||
public async Task OnlyHandles_UnhandledExceptions(HttpStatusCode expectedStatusCode)
|
||||
{
|
||||
using (var server = TestServer.Create(app =>
|
||||
{
|
||||
app.UseErrorHandler("/handle-errors");
|
||||
|
||||
app.Map("/handle-errors", (innerAppBuilder) =>
|
||||
{
|
||||
innerAppBuilder.Run(async (httpContext) =>
|
||||
{
|
||||
await httpContext.Response.WriteAsync("Handled error in a custom way.");
|
||||
});
|
||||
});
|
||||
|
||||
app.Run((RequestDelegate)(async (context) =>
|
||||
{
|
||||
context.Response.StatusCode = (int)expectedStatusCode;
|
||||
context.Response.ContentType = "text/plain; charset=utf-8";
|
||||
await context.Response.WriteAsync("An error occurred while adding a product");
|
||||
}));
|
||||
}))
|
||||
{
|
||||
var client = server.CreateClient();
|
||||
var response = await client.GetAsync(string.Empty);
|
||||
Assert.Equal(expectedStatusCode, response.StatusCode);
|
||||
Assert.Equal("An error occurred while adding a product", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DoesNotHandle_UnhandledExceptions_WhenResponseAlreadyStarted()
|
||||
{
|
||||
using (var server = TestServer.Create(app =>
|
||||
{
|
||||
app.Use(async (httpContext, next) =>
|
||||
{
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
await next();
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
Assert.NotNull(exception);
|
||||
Assert.Equal("Something bad happened", exception.Message);
|
||||
});
|
||||
|
||||
app.UseErrorHandler("/handle-errors");
|
||||
|
||||
app.Map("/handle-errors", (innerAppBuilder) =>
|
||||
{
|
||||
innerAppBuilder.Run(async (httpContext) =>
|
||||
{
|
||||
await httpContext.Response.WriteAsync("Handled error in a custom way.");
|
||||
});
|
||||
});
|
||||
|
||||
app.Run(async (httpContext) =>
|
||||
{
|
||||
await httpContext.Response.WriteAsync("Hello");
|
||||
throw new InvalidOperationException("Something bad happened");
|
||||
});
|
||||
}))
|
||||
{
|
||||
var client = server.CreateClient();
|
||||
var response = await client.GetAsync(string.Empty);
|
||||
response.EnsureSuccessStatusCode();
|
||||
Assert.Equal("Hello", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ClearsCacheHeaders_SetByReexecutionPathHandlers()
|
||||
{
|
||||
var expiresTime = DateTime.UtcNow.AddDays(5).ToString("R");
|
||||
var expectedResponseBody = "Handled error in a custom way.";
|
||||
using (var server = TestServer.Create(app =>
|
||||
{
|
||||
app.UseErrorHandler("/handle-errors");
|
||||
|
||||
app.Map("/handle-errors", (innerAppBuilder) =>
|
||||
{
|
||||
innerAppBuilder.Run(async (httpContext) =>
|
||||
{
|
||||
httpContext.Response.Headers.Add("Cache-Control", new[] { "max-age=600" });
|
||||
httpContext.Response.Headers.Add("Pragma", new[] { "max-age=600" });
|
||||
httpContext.Response.Headers.Add(
|
||||
"Expires", new[] { expiresTime });
|
||||
httpContext.Response.Headers.Add("ETag", new[] { "12345" });
|
||||
|
||||
await httpContext.Response.WriteAsync(expectedResponseBody);
|
||||
});
|
||||
});
|
||||
|
||||
app.Run((context) =>
|
||||
{
|
||||
throw new InvalidOperationException("Invalid input provided.");
|
||||
});
|
||||
}))
|
||||
{
|
||||
var client = server.CreateClient();
|
||||
var response = await client.GetAsync(string.Empty);
|
||||
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
|
||||
Assert.Equal(expectedResponseBody, await response.Content.ReadAsStringAsync());
|
||||
IEnumerable<string> values;
|
||||
Assert.True(response.Headers.TryGetValues("Cache-Control", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal("no-cache", values.First());
|
||||
Assert.True(response.Headers.TryGetValues("Pragma", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal("no-cache", values.First());
|
||||
Assert.True(response.Content.Headers.TryGetValues("Expires", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal("-1", values.First());
|
||||
Assert.False(response.Headers.TryGetValues("ETag", out values));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DoesNotModifyCacheHeaders_WhenNoExceptionIsThrown()
|
||||
{
|
||||
var expiresTime = DateTime.UtcNow.AddDays(10).ToString("R");
|
||||
var expectedResponseBody = "Hello world!";
|
||||
using (var server = TestServer.Create(app =>
|
||||
{
|
||||
app.UseErrorHandler("/handle-errors");
|
||||
|
||||
app.Map("/handle-errors", (innerAppBuilder) =>
|
||||
{
|
||||
innerAppBuilder.Run(async (httpContext) =>
|
||||
{
|
||||
await httpContext.Response.WriteAsync("Handled error in a custom way.");
|
||||
});
|
||||
});
|
||||
|
||||
app.Run(async (httpContext) =>
|
||||
{
|
||||
httpContext.Response.Headers.Add("Cache-Control", new[] { "max-age=3600" });
|
||||
httpContext.Response.Headers.Add("Pragma", new[] { "max-age=3600" });
|
||||
httpContext.Response.Headers.Add("Expires", new[] { expiresTime });
|
||||
httpContext.Response.Headers.Add("ETag", new[] { "abcdef" });
|
||||
|
||||
await httpContext.Response.WriteAsync(expectedResponseBody);
|
||||
});
|
||||
}))
|
||||
{
|
||||
var client = server.CreateClient();
|
||||
var response = await client.GetAsync(string.Empty);
|
||||
response.EnsureSuccessStatusCode();
|
||||
Assert.Equal(expectedResponseBody, await response.Content.ReadAsStringAsync());
|
||||
IEnumerable<string> values;
|
||||
Assert.True(response.Headers.TryGetValues("Cache-Control", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal("max-age=3600", values.First());
|
||||
Assert.True(response.Headers.TryGetValues("Pragma", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal("max-age=3600", values.First());
|
||||
Assert.True(response.Content.Headers.TryGetValues("Expires", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal(expiresTime, values.First());
|
||||
Assert.True(response.Headers.TryGetValues("ETag", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal("abcdef", values.First());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DoesNotClearCacheHeaders_WhenResponseHasAlreadyStarted()
|
||||
{
|
||||
var expiresTime = DateTime.UtcNow.AddDays(10).ToString("R");
|
||||
using (var server = TestServer.Create(app =>
|
||||
{
|
||||
app.Use(async (httpContext, next) =>
|
||||
{
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
await next();
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
Assert.NotNull(exception);
|
||||
Assert.Equal("Something bad happened", exception.Message);
|
||||
});
|
||||
|
||||
app.UseErrorHandler("/handle-errors");
|
||||
|
||||
app.Map("/handle-errors", (innerAppBuilder) =>
|
||||
{
|
||||
innerAppBuilder.Run(async (httpContext) =>
|
||||
{
|
||||
await httpContext.Response.WriteAsync("Handled error in a custom way.");
|
||||
});
|
||||
});
|
||||
|
||||
app.Run(async (httpContext) =>
|
||||
{
|
||||
httpContext.Response.Headers.Add("Cache-Control", new[] { "max-age=3600" });
|
||||
httpContext.Response.Headers.Add("Pragma", new[] { "max-age=3600" });
|
||||
httpContext.Response.Headers.Add("Expires", new[] { expiresTime });
|
||||
httpContext.Response.Headers.Add("ETag", new[] { "abcdef" });
|
||||
|
||||
await httpContext.Response.WriteAsync("Hello");
|
||||
|
||||
throw new InvalidOperationException("Something bad happened");
|
||||
});
|
||||
}))
|
||||
{
|
||||
var client = server.CreateClient();
|
||||
var response = await client.GetAsync(string.Empty);
|
||||
response.EnsureSuccessStatusCode();
|
||||
Assert.Equal("Hello", await response.Content.ReadAsStringAsync());
|
||||
IEnumerable<string> values;
|
||||
Assert.True(response.Headers.TryGetValues("Cache-Control", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal("max-age=3600", values.First());
|
||||
Assert.True(response.Headers.TryGetValues("Pragma", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal("max-age=3600", values.First());
|
||||
Assert.True(response.Content.Headers.TryGetValues("Expires", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal(expiresTime, values.First());
|
||||
Assert.True(response.Headers.TryGetValues("ETag", out values));
|
||||
Assert.Single(values);
|
||||
Assert.Equal("abcdef", values.First());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Diagnostics.Elm": "1.0.0-*",
|
||||
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
||||
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
||||
"xunit.runner.aspnet": "2.0.0-aspnet-*"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue