Adding HttpResponseException to WebApi Shim.
This commit is contained in:
parent
d915994f0b
commit
ebf64ce4e3
|
|
@ -0,0 +1,36 @@
|
||||||
|
// 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;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
|
{
|
||||||
|
public class HttpResponseException : Exception
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="HttpResponseException"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="statusCode">The status code of the response.</param>
|
||||||
|
public HttpResponseException(HttpStatusCode statusCode)
|
||||||
|
: this(new HttpResponseMessage(statusCode))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="HttpResponseException"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="response">The response message.</param>
|
||||||
|
public HttpResponseException([NotNull] HttpResponseMessage response)
|
||||||
|
: base(Resources.HttpResponseExceptionMessage)
|
||||||
|
{
|
||||||
|
Response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="HttpResponseMessage"/> to return to the client.
|
||||||
|
/// </summary>
|
||||||
|
public HttpResponseMessage Response { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// 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.Net.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An action filter which sets <see cref="ActionExecutedContext.Result"/> to an <see cref="ObjectResult"/>
|
||||||
|
/// if the exception type is <see cref="HttpResponseException"/>.
|
||||||
|
/// This filter runs immediately after the action.
|
||||||
|
/// </summary>
|
||||||
|
public class HttpResponseExceptionActionFilter : IActionFilter, IOrderedFilter
|
||||||
|
{
|
||||||
|
// Return a high number by default so that it runs closest to the action.
|
||||||
|
public int Order { get; set; } = int.MaxValue - 10;
|
||||||
|
|
||||||
|
public void OnActionExecuting([NotNull] ActionExecutingContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnActionExecuted([NotNull] ActionExecutedContext context)
|
||||||
|
{
|
||||||
|
var httpResponseException = context.Exception as HttpResponseException;
|
||||||
|
if (httpResponseException != null)
|
||||||
|
{
|
||||||
|
var request = context.HttpContext.GetHttpRequestMessage();
|
||||||
|
var response = httpResponseException.Response;
|
||||||
|
|
||||||
|
if (response != null && response.RequestMessage == null)
|
||||||
|
{
|
||||||
|
response.RequestMessage = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
var objectResult = new ObjectResult(response)
|
||||||
|
{
|
||||||
|
DeclaredType = typeof(HttpResponseMessage)
|
||||||
|
};
|
||||||
|
|
||||||
|
context.Result = objectResult;
|
||||||
|
|
||||||
|
// Its marked as handled as in webapi because an HttpResponseException
|
||||||
|
// was considered as a 'success' response.
|
||||||
|
context.ExceptionHandled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -138,6 +138,22 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("MaxHttpCollectionKeyLimitReached"), p0, p1);
|
return string.Format(CultureInfo.CurrentCulture, GetString("MaxHttpCollectionKeyLimitReached"), p0, p1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
|
||||||
|
/// </summary>
|
||||||
|
internal static string HttpResponseExceptionMessage
|
||||||
|
{
|
||||||
|
get { return GetString("HttpResponseExceptionMessage"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatHttpResponseExceptionMessage()
|
||||||
|
{
|
||||||
|
return GetString("HttpResponseExceptionMessage");
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -141,4 +141,7 @@
|
||||||
<data name="MaxHttpCollectionKeyLimitReached" xml:space="preserve">
|
<data name="MaxHttpCollectionKeyLimitReached" xml:space="preserve">
|
||||||
<value>The number of keys in a NameValueCollection has exceeded the limit of '{0}'. You can adjust it by modifying the MaxHttpCollectionKeys property on the '{1}' class.</value>
|
<value>The number of keys in a NameValueCollection has exceeded the limit of '{0}'. You can adjust it by modifying the MaxHttpCollectionKeys property on the '{1}' class.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HttpResponseExceptionMessage" xml:space="preserve">
|
||||||
|
<value>Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -25,6 +25,9 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
options.ApplicationModelConventions.Add(new WebApiOverloadingGlobalModelConvention());
|
options.ApplicationModelConventions.Add(new WebApiOverloadingGlobalModelConvention());
|
||||||
options.ApplicationModelConventions.Add(new WebApiRoutesGlobalModelConvention(area: DefaultAreaName));
|
options.ApplicationModelConventions.Add(new WebApiRoutesGlobalModelConvention(area: DefaultAreaName));
|
||||||
|
|
||||||
|
// Add an action filter for handling the HttpResponseException.
|
||||||
|
options.Filters.Add(new HttpResponseExceptionActionFilter());
|
||||||
|
|
||||||
// Add a model binder to be able to bind HttpRequestMessage
|
// Add a model binder to be able to bind HttpRequestMessage
|
||||||
options.ModelBinders.Insert(0, new HttpRequestMessageModelBinder());
|
options.ModelBinders.Insert(0, new HttpRequestMessageModelBinder());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,78 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal(expected, formatters);
|
Assert.Equal(expected, formatters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ActionThrowsHttpResponseException_WithStatusCode()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync(
|
||||||
|
"http://localhost/api/Blog/HttpResponseException/ThrowsHttpResponseExceptionWithHttpStatusCode");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(string.Empty, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ActionThrowsHttpResponseException_WithResponse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync(
|
||||||
|
"http://localhost/api/Blog/HttpResponseException"+
|
||||||
|
"/ThrowsHttpResponseExceptionWithHttpResponseMessage?message=send some message");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("send some message", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ActionThrowsHttpResponseException_EnsureGlobalHttpresponseExceptionActionFilter_IsInvoked()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync(
|
||||||
|
"http://localhost/api/Blog/HttpResponseException/ThrowsHttpResponseExceptionEnsureGlobalFilterRunsLast");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Ensure we do not get a no content result.
|
||||||
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(string.Empty, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ActionThrowsHttpResponseException_EnsureGlobalFilterConvention_IsApplied()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync(
|
||||||
|
"http://localhost/api/Blog/"+
|
||||||
|
"HttpResponseException/ThrowsHttpResponseExceptionInjectAFilterToHandleHttpResponseException");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Ensure we do get a no content result.
|
||||||
|
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(string.Empty, content);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ApiController_CanValidateCustomObjectWithPrefix_Fails()
|
public async Task ApiController_CanValidateCustomObjectWithPrefix_Fails()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
|
{
|
||||||
|
public class HttpResponseExceptionActionFilterTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void OnActionExecuting_IsNoOp()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = new HttpResponseExceptionActionFilter();
|
||||||
|
var context = new ActionExecutingContext(new ActionContext(
|
||||||
|
new DefaultHttpContext(),
|
||||||
|
new RouteData(),
|
||||||
|
actionDescriptor: Mock.Of<ActionDescriptor>()),
|
||||||
|
filters: Mock.Of<IList<IFilter>>(),
|
||||||
|
actionArguments: new Dictionary<string, object>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
filter.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(context.Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OrderIsSetToMaxValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = new HttpResponseExceptionActionFilter();
|
||||||
|
var expectedFilterOrder = int.MaxValue - 10;
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Equal(expectedFilterOrder, filter.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OnActionExecuted_HandlesExceptionAndReturnsObjectResult()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = new HttpResponseExceptionActionFilter();
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.Request.Method = "GET";
|
||||||
|
|
||||||
|
var context = new ActionExecutedContext(
|
||||||
|
new ActionContext(
|
||||||
|
httpContext,
|
||||||
|
new RouteData(),
|
||||||
|
actionDescriptor: Mock.Of<ActionDescriptor>()),
|
||||||
|
filters: null);
|
||||||
|
context.Exception = new HttpResponseException(HttpStatusCode.BadRequest);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
filter.OnActionExecuted(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(context.ExceptionHandled);
|
||||||
|
var result = Assert.IsType<ObjectResult>(context.Result);
|
||||||
|
Assert.Equal(typeof(HttpResponseMessage), result.DeclaredType);
|
||||||
|
var response = Assert.IsType<HttpResponseMessage>(result.Value);
|
||||||
|
Assert.NotNull(response.RequestMessage);
|
||||||
|
Assert.Equal(context.HttpContext.GetHttpRequestMessage(), response.RequestMessage);
|
||||||
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// 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.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
|
{
|
||||||
|
public class HttpResponseExceptionTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
[ReplaceCulture]
|
||||||
|
public void Constructor_SetsResponseProperty()
|
||||||
|
{
|
||||||
|
// Arrange and Act
|
||||||
|
var response = new HttpResponseMessage();
|
||||||
|
var exception = new HttpResponseException(response);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(response, exception.Response);
|
||||||
|
Assert.Equal("Processing of the HTTP request resulted in an exception."+
|
||||||
|
" Please see the HTTP response returned by the 'Response' "+
|
||||||
|
"property of this exception for details.",
|
||||||
|
exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[ReplaceCulture]
|
||||||
|
public void Constructor_SetsResponsePropertyWithGivenStatusCode()
|
||||||
|
{
|
||||||
|
// Arrange and Act
|
||||||
|
var exception = new HttpResponseException(HttpStatusCode.BadGateway);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.BadGateway, exception.Response.StatusCode);
|
||||||
|
Assert.Equal("Processing of the HTTP request resulted in an exception."+
|
||||||
|
" Please see the HTTP response returned by the 'Response' "+
|
||||||
|
"property of this exception for details.",
|
||||||
|
exception.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||||
"Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-*",
|
"Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Testing": "1.0.0-*",
|
||||||
"Moq": "4.2.1312.1622",
|
"Moq": "4.2.1312.1622",
|
||||||
"Xunit.KRunner": "1.0.0-*"
|
"Xunit.KRunner": "1.0.0-*"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
// 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.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Web.Http;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.WebApiCompatShim;
|
||||||
|
|
||||||
|
namespace WebApiCompatShimWebSite
|
||||||
|
{
|
||||||
|
public class HttpResponseExceptionController : ApiController
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public object ThrowsHttpResponseExceptionWithHttpStatusCode()
|
||||||
|
{
|
||||||
|
throw new HttpResponseException(HttpStatusCode.BadRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public object ThrowsHttpResponseExceptionWithHttpResponseMessage(string message)
|
||||||
|
{
|
||||||
|
var httpResponse = new HttpResponseMessage();
|
||||||
|
httpResponse.Content = new StringContent(message);
|
||||||
|
throw new HttpResponseException(httpResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestActionFilter]
|
||||||
|
[HttpGet]
|
||||||
|
public object ThrowsHttpResponseExceptionEnsureGlobalFilterRunsLast()
|
||||||
|
{
|
||||||
|
throw new HttpResponseException(HttpStatusCode.BadRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs before the HttpResponseExceptionActionFilter's OnActionExecuted.
|
||||||
|
[TestActionFilter(Order = int.MaxValue)]
|
||||||
|
[HttpGet]
|
||||||
|
public object ThrowsHttpResponseExceptionInjectAFilterToHandleHttpResponseException()
|
||||||
|
{
|
||||||
|
throw new HttpResponseException(HttpStatusCode.BadRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestActionFilterAttribute : ActionFilterAttribute
|
||||||
|
{
|
||||||
|
public override void OnActionExecuted(ActionExecutedContext context)
|
||||||
|
{
|
||||||
|
if (!context.ExceptionHandled)
|
||||||
|
{
|
||||||
|
var httpResponseException = context.Exception as HttpResponseException;
|
||||||
|
if (httpResponseException != null)
|
||||||
|
{
|
||||||
|
context.Result = new NoContentResult();
|
||||||
|
context.ExceptionHandled = true;
|
||||||
|
|
||||||
|
// Null it out so that next filter do not handle it.
|
||||||
|
context.Exception = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
|
@ -16,7 +17,6 @@ namespace WebApiCompatShimWebSite
|
||||||
app.UseServices(services =>
|
app.UseServices(services =>
|
||||||
{
|
{
|
||||||
services.AddMvc(configuration);
|
services.AddMvc(configuration);
|
||||||
|
|
||||||
services.AddWebApiConventions();
|
services.AddWebApiConventions();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue