Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Limit the maximum number of Model errors to a reasonable value.
Browse files Browse the repository at this point in the history
Fixes #490
  • Loading branch information
pranavkm committed Sep 15, 2014
1 parent a468986 commit c86fc51
Show file tree
Hide file tree
Showing 14 changed files with 407 additions and 22 deletions.
19 changes: 19 additions & 0 deletions src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class MvcOptions
{
private AntiForgeryOptions _antiForgeryOptions = new AntiForgeryOptions();
private RazorViewEngineOptions _viewEngineOptions = new RazorViewEngineOptions();
private int _maxModelStateErrors = 200;

public MvcOptions()
{
Expand Down Expand Up @@ -85,6 +86,24 @@ public RazorViewEngineOptions ViewEngineOptions
}
}

/// <summary>
/// Gets or sets the maximum number of validation errors that are allowed by this application before further
/// errors are ignored.
/// </summary>
public int MaxModelValidationErrors
{
get { return _maxModelStateErrors; }
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("MaxModelValidationErrors");
}

_maxModelStateErrors = value;
}
}

/// <summary>
/// Get a list of the <see cref="ModelBinderDescriptor" /> used by the
/// <see cref="ModelBinding.CompositeModelBinder" />.
Expand Down
18 changes: 11 additions & 7 deletions src/Microsoft.AspNet.Mvc.Core/MvcRouteHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel;

namespace Microsoft.AspNet.Mvc
{
Expand All @@ -32,7 +33,7 @@ public string GetVirtualPath([NotNull] VirtualPathContext context)
public async Task RouteAsync([NotNull] RouteContext context)
{
var services = context.HttpContext.RequestServices;

// Verify if AddMvc was done before calling UseMvc
// We use the MvcMarkerService to make sure if all the services were added.
MvcServicesHelper.ThrowIfMvcNotRegistered(services);
Expand Down Expand Up @@ -64,19 +65,22 @@ public async Task RouteAsync([NotNull] RouteContext context)
return;
}

if (actionDescriptor.RouteValueDefaults != null)
{
foreach (var kvp in actionDescriptor.RouteValueDefaults)
if (actionDescriptor.RouteValueDefaults != null)
{
if (!context.RouteData.Values.ContainsKey(kvp.Key))
foreach (var kvp in actionDescriptor.RouteValueDefaults)
{
context.RouteData.Values.Add(kvp.Key, kvp.Value);
if (!context.RouteData.Values.ContainsKey(kvp.Key))
{
context.RouteData.Values.Add(kvp.Key, kvp.Value);
}
}
}
}

var actionContext = new ActionContext(context.HttpContext, context.RouteData, actionDescriptor);

var optionsAccessor = services.GetService<IOptionsAccessor<MvcOptions>>();
actionContext.ModelState.MaxAllowedErrors = optionsAccessor.Options.MaxModelValidationErrors;

var contextAccessor = services.GetService<IContextAccessor<ActionContext>>();
using (contextAccessor.SetContextSource(() => actionContext, PreventExchange))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ public virtual async Task<bool> BindModelAsync([NotNull] ModelBindingContext bin

// Only perform validation at the root of the object graph. ValidationNode will recursively walk the graph.
// Ignore ComplexModelDto since it essentially wraps the primary object.
if (newBindingContext.ModelMetadata.ContainerType == null &&
newBindingContext.ModelMetadata.ModelType != typeof(ComplexModelDto))
if (IsBindingAtRootOfObjectGraph(newBindingContext))
{
// run validation and return the model
// If we fell back to an empty prefix above and are dealing with simple types,
Expand All @@ -85,31 +84,57 @@ public virtual async Task<bool> BindModelAsync([NotNull] ModelBindingContext bin
bindingContext.ModelMetadata,
containerMetadata: null);

newBindingContext.ValidationNode.Validate(validationContext, parentNode: null);
try
{
newBindingContext.ValidationNode.Validate(validationContext, parentNode: null);
}
catch (TooManyModelErrorsException)
{
}
}

bindingContext.Model = newBindingContext.Model;
return true;
}

private async Task<bool> TryBind([NotNull] ModelBindingContext bindingContext)
private async Task<bool> TryBind(ModelBindingContext bindingContext)
{
// TODO: RuntimeHelpers.EnsureSufficientExecutionStack does not exist in the CoreCLR.
// Protects against stack overflow for deeply nested model binding
// RuntimeHelpers.EnsureSufficientExecutionStack();

foreach (var binder in ModelBinders)
{
if (await binder.BindModelAsync(bindingContext))
try
{
if (await binder.BindModelAsync(bindingContext))
{
return true;
}
}
catch (TooManyModelErrorsException)
{
return true;
if (!IsBindingAtRootOfObjectGraph(bindingContext))
{
throw;
}
}
}

// Either we couldn't find a binder, or the binder couldn't bind. Distinction is not important.
return false;
}

private static bool IsBindingAtRootOfObjectGraph(ModelBindingContext bindingContext)
{
// We're at the root of the object graph if the model does does not have a container.
// This statement is true for complex types at the root twice over - once with the actual model
// and once when when it is represented by a ComplexModelDto. Ignore the latter case.

return bindingContext.ModelMetadata.ContainerType == null &&
bindingContext.ModelMetadata.ModelType != typeof(ComplexModelDto);
}

private static ModelBindingContext CreateNewBindingContext(ModelBindingContext oldBindingContext,
string modelName,
bool reuseValidationNode)
Expand Down
Loading

0 comments on commit c86fc51

Please sign in to comment.