This repository was archived by the owner on Dec 14, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Fixes #3874] Null passed as arguments to controller method parameter…
…s when no InputFormatter matches * Add an UnsupportedContentType to the ModelState dictionary when no formatter can read the body. * Add a filter to the pipeline that searches for that specific exception and transforms the response into 415.
- Loading branch information
Showing
15 changed files
with
332 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/UnsupportedContentTypeException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
using Microsoft.AspNetCore.Mvc.Formatters; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.ModelBinding | ||
{ | ||
/// <summary> | ||
/// The <see cref="Exception"/> that is added to model state when a model binder for the body of the request is | ||
/// unable to understand the request content type header. | ||
/// </summary> | ||
public class UnsupportedContentTypeException : Exception | ||
{ | ||
/// <summary> | ||
/// Creates a new instance of <see cref="UnsupportedContentTypeException"/> with the specified | ||
/// exception <paramref name="message"/>. | ||
/// </summary> | ||
/// <param name="message">The message that describes the error.</param> | ||
public UnsupportedContentTypeException(string message) | ||
: base(message) | ||
{ | ||
if (message == null) | ||
{ | ||
throw new ArgumentNullException(nameof(message)); | ||
} | ||
} | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/UnsupportedContentTypeFilter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// 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 Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Mvc.Filters; | ||
using Microsoft.Net.Http.Headers; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.ModelBinding | ||
{ | ||
/// <summary> | ||
/// A filter that scans for <see cref="UnsupportedContentTypeException"/> in the | ||
/// <see cref="ActionContext.ModelState"/> and shortcircuits the pipeline | ||
/// with an Unsupported Media Type (415) response. | ||
/// </summary> | ||
public class UnsupportedContentTypeFilter : IActionFilter | ||
{ | ||
/// <inheritdoc /> | ||
public void OnActionExecuting(ActionExecutingContext context) | ||
{ | ||
if (HasUnsupportedContentTypeError(context)) | ||
{ | ||
context.Result = new UnsupportedMediaTypeResult(); | ||
} | ||
} | ||
|
||
private bool HasUnsupportedContentTypeError(ActionExecutingContext context) | ||
{ | ||
var modelState = context.ModelState; | ||
if (modelState.IsValid) | ||
{ | ||
return false; | ||
} | ||
|
||
foreach (var kvp in modelState) | ||
{ | ||
var errors = kvp.Value.Errors; | ||
for (int i = 0; i < errors.Count; i++) | ||
{ | ||
var error = errors[i]; | ||
if (error.Exception is UnsupportedContentTypeException) | ||
{ | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public void OnActionExecuted(ActionExecutedContext context) | ||
{ | ||
} | ||
} | ||
} |
132 changes: 132 additions & 0 deletions
132
test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/UnsupportedContentTypeFilterTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// 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 Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Http.Internal; | ||
using Microsoft.AspNetCore.Mvc.Abstractions; | ||
using Microsoft.AspNetCore.Mvc.Filters; | ||
using Microsoft.AspNetCore.Routing; | ||
using Xunit; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.ModelBinding | ||
{ | ||
public class UnsupportedContentTypeFilterTest | ||
{ | ||
[Fact] | ||
public void OnActionExecuting_ChangesActionResult_IfUnsupportedContentTypeExceptionIsFoundOnModelState() | ||
{ | ||
// Arrange | ||
var context = new ActionExecutingContext( | ||
new ActionContext | ||
{ | ||
HttpContext = new DefaultHttpContext(), | ||
RouteData = new RouteData(), | ||
ActionDescriptor = new ActionDescriptor() | ||
}, | ||
new List<IFilterMetadata>(), | ||
new Dictionary<string, object>(), | ||
new object()); | ||
|
||
var modelMetadata = new EmptyModelMetadataProvider() | ||
.GetMetadataForType(typeof(int)); | ||
|
||
context.ModelState.AddModelError( | ||
"person.body", | ||
new UnsupportedContentTypeException("error"), | ||
modelMetadata); | ||
|
||
var filter = new UnsupportedContentTypeFilter(); | ||
|
||
// Act | ||
filter.OnActionExecuting(context); | ||
|
||
// Assert | ||
Assert.NotNull(context.Result); | ||
var status = Assert.IsType<UnsupportedMediaTypeResult>(context.Result); | ||
} | ||
|
||
[Fact] | ||
public void OnActionExecuting_DoesNotChangeActionResult_IfOtherErrorsAreFoundOnModelState() | ||
{ | ||
// Arrange | ||
var context = new ActionExecutingContext( | ||
new ActionContext | ||
{ | ||
HttpContext = new DefaultHttpContext(), | ||
RouteData = new RouteData(), | ||
ActionDescriptor = new ActionDescriptor() | ||
}, | ||
new List<IFilterMetadata>(), | ||
new Dictionary<string, object>(), | ||
new object()); | ||
|
||
context.ModelState.AddModelError("person.body", "Some error"); | ||
|
||
var filter = new UnsupportedContentTypeFilter(); | ||
|
||
// Act | ||
filter.OnActionExecuting(context); | ||
|
||
// Assert | ||
Assert.Null(context.Result); | ||
} | ||
|
||
[Fact] | ||
public void OnActionExecuting_DoesNotChangeActionResult_IfModelStateIsValid() | ||
{ | ||
// Arrange | ||
var context = new ActionExecutingContext( | ||
new ActionContext | ||
{ | ||
HttpContext = new DefaultHttpContext(), | ||
RouteData = new RouteData(), | ||
ActionDescriptor = new ActionDescriptor() | ||
}, | ||
new List<IFilterMetadata>(), | ||
new Dictionary<string, object>(), | ||
new object()); | ||
|
||
var filter = new UnsupportedContentTypeFilter(); | ||
|
||
// Act | ||
filter.OnActionExecuting(context); | ||
|
||
// Assert | ||
Assert.Null(context.Result); | ||
} | ||
|
||
[Fact] | ||
public void OnActionExecuting_DoesNotChangeActionResult_IfOtherExceptionsAreFoundOnModelState() | ||
{ | ||
// Arrange | ||
var context = new ActionExecutingContext( | ||
new ActionContext | ||
{ | ||
HttpContext = new DefaultHttpContext(), | ||
RouteData = new RouteData(), | ||
ActionDescriptor = new ActionDescriptor() | ||
}, | ||
new List<IFilterMetadata>(), | ||
new Dictionary<string, object>(), | ||
new object()); | ||
|
||
var modelMetadata = new EmptyModelMetadataProvider() | ||
.GetMetadataForType(typeof(int)); | ||
|
||
context.ModelState.AddModelError( | ||
"person.body", | ||
new Exception("error"), | ||
modelMetadata); | ||
|
||
var filter = new UnsupportedContentTypeFilter(); | ||
|
||
// Act | ||
filter.OnActionExecuting(context); | ||
|
||
// Assert | ||
Assert.Null(context.Result); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.