Parity between Controller and Page/PageModel
This commit is contained in:
parent
3e8cd1e7c9
commit
c9bfd2296d
|
|
@ -52,7 +52,26 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
var factories = controllerContext.ValueProviderFactories;
|
var factories = controllerContext.ValueProviderFactories;
|
||||||
var valueProviderFactoryContext = new ValueProviderFactoryContext(controllerContext);
|
|
||||||
|
return await CreateAsync(controllerContext, factories);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously creates a <see cref="CompositeValueProvider"/> using the provided
|
||||||
|
/// <paramref name="actionContext"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="actionContext">The <see cref="ActionContext"/> associated with the current request.</param>
|
||||||
|
/// <param name="factories">The <see cref="IValueProviderFactory"/> to be applied to the context.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="Task{TResult}"/> which, when completed, asynchronously returns a
|
||||||
|
/// <see cref="CompositeValueProvider"/>.
|
||||||
|
/// </returns>
|
||||||
|
public static async Task<CompositeValueProvider> CreateAsync(
|
||||||
|
ActionContext actionContext,
|
||||||
|
IList<IValueProviderFactory> factories)
|
||||||
|
{
|
||||||
|
var valueProviderFactoryContext = new ValueProviderFactoryContext(actionContext);
|
||||||
|
|
||||||
for (var i = 0; i < factories.Count; i++)
|
for (var i = 0; i < factories.Count; i++)
|
||||||
{
|
{
|
||||||
var factory = factories[i];
|
var factory = factories[i];
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,19 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Authentication;
|
using Microsoft.AspNetCore.Http.Authentication;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor;
|
using Microsoft.AspNetCore.Mvc.Razor;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -23,6 +28,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
public abstract class Page : RazorPageBase, IRazorPage
|
public abstract class Page : RazorPageBase, IRazorPage
|
||||||
{
|
{
|
||||||
private PageArgumentBinder _binder;
|
private PageArgumentBinder _binder;
|
||||||
|
private IObjectModelValidator _objectValidator;
|
||||||
|
private IModelMetadataProvider _metadataProvider;
|
||||||
|
private IModelBinderFactory _modelBinderFactory;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="RazorPages.PageContext"/>.
|
/// The <see cref="RazorPages.PageContext"/>.
|
||||||
|
|
@ -80,11 +88,61 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="AspNetCore.Routing.RouteData"/> for the executing action.
|
||||||
|
/// </summary>
|
||||||
|
public RouteData RouteData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return PageContext.RouteData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="ModelStateDictionary"/>.
|
/// Gets the <see cref="ModelStateDictionary"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ModelStateDictionary ModelState => PageContext?.ModelState;
|
public ModelStateDictionary ModelState => PageContext?.ModelState;
|
||||||
|
|
||||||
|
private IObjectModelValidator ObjectValidator
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_objectValidator == null)
|
||||||
|
{
|
||||||
|
_objectValidator = HttpContext?.RequestServices?.GetRequiredService<IObjectModelValidator>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _objectValidator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IModelMetadataProvider MetadataProvider
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_metadataProvider == null)
|
||||||
|
{
|
||||||
|
_metadataProvider = HttpContext?.RequestServices?.GetRequiredService<IModelMetadataProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _metadataProvider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IModelBinderFactory ModelBinderFactory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_modelBinderFactory == null)
|
||||||
|
{
|
||||||
|
_modelBinderFactory = HttpContext?.RequestServices?.GetRequiredService<IModelBinderFactory>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _modelBinderFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void EnsureRenderedBodyOrSections()
|
public override void EnsureRenderedBodyOrSections()
|
||||||
{
|
{
|
||||||
|
|
@ -1130,5 +1188,384 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
return new PageViewResult(this);
|
return new PageViewResult(this);
|
||||||
}
|
}
|
||||||
#endregion Factory methods
|
#endregion Factory methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="Page"/>'s current
|
||||||
|
/// <see cref="IValueProvider"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
public virtual Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryUpdateModelAsync(model, prefix: string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="Page"/>'s current
|
||||||
|
/// <see cref="IValueProvider"/> and a <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the current <see cref="IValueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
public virtual async Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string prefix)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefix == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(prefix));
|
||||||
|
}
|
||||||
|
var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
|
||||||
|
return await TryUpdateModelAsync(model, prefix, valueProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the <paramref name="valueProvider"/> and a
|
||||||
|
/// <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
public virtual Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string prefix,
|
||||||
|
IValueProvider valueProvider)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefix == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(valueProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="Page"/>'s current
|
||||||
|
/// <see cref="IValueProvider"/> and a <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the current <see cref="IValueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="includeExpressions"> <see cref="Expression"/>(s) which represent top-level properties
|
||||||
|
/// which need to be included for the current model.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
public async Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string prefix,
|
||||||
|
params Expression<Func<TModel, object>>[] includeExpressions)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeExpressions == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(includeExpressions));
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
|
||||||
|
return await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
includeExpressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="Page"/>'s current
|
||||||
|
/// <see cref="IValueProvider"/> and a <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the current <see cref="IValueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="propertyFilter">A predicate which can be used to filter properties at runtime.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
public async Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string prefix,
|
||||||
|
Func<ModelMetadata, bool> propertyFilter)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyFilter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(propertyFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
|
||||||
|
return await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
propertyFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the <paramref name="valueProvider"/> and a
|
||||||
|
/// <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <param name="includeExpressions"> <see cref="Expression"/>(s) which represent top-level properties
|
||||||
|
/// which need to be included for the current model.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
public Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string prefix,
|
||||||
|
IValueProvider valueProvider,
|
||||||
|
params Expression<Func<TModel, object>>[] includeExpressions)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(valueProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeExpressions == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(includeExpressions));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
includeExpressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the <paramref name="valueProvider"/> and a
|
||||||
|
/// <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <param name="propertyFilter">A predicate which can be used to filter properties at runtime.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
public Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string prefix,
|
||||||
|
IValueProvider valueProvider,
|
||||||
|
Func<ModelMetadata, bool> propertyFilter)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(valueProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyFilter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(propertyFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
propertyFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="Page"/>'s current
|
||||||
|
/// <see cref="IValueProvider"/> and a <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="modelType">The type of model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the current <see cref="IValueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
public virtual async Task<bool> TryUpdateModelAsync(
|
||||||
|
object model,
|
||||||
|
Type modelType,
|
||||||
|
string prefix)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(modelType));
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
|
||||||
|
return await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
modelType,
|
||||||
|
prefix,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the <paramref name="valueProvider"/> and a
|
||||||
|
/// <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="modelType">The type of model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <param name="propertyFilter">A predicate which can be used to filter properties at runtime.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
public Task<bool> TryUpdateModelAsync(
|
||||||
|
object model,
|
||||||
|
Type modelType,
|
||||||
|
string prefix,
|
||||||
|
IValueProvider valueProvider,
|
||||||
|
Func<ModelMetadata, bool> propertyFilter)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(modelType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(valueProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyFilter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(propertyFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
modelType,
|
||||||
|
prefix,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
propertyFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the specified <paramref name="model"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model to validate.</param>
|
||||||
|
/// <returns><c>true</c> if the <see cref="ModelState"/> is valid; <c>false</c> otherwise.</returns>
|
||||||
|
public virtual bool TryValidateModel(
|
||||||
|
object model)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryValidateModel(model, prefix: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the specified <paramref name="model"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model to validate.</param>
|
||||||
|
/// <param name="prefix">The key to use when looking up information in <see cref="ModelState"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <returns><c>true</c> if the <see cref="ModelState"/> is valid;<c>false</c> otherwise.</returns>
|
||||||
|
public virtual bool TryValidateModel(
|
||||||
|
object model,
|
||||||
|
string prefix)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectValidator.Validate(
|
||||||
|
PageContext,
|
||||||
|
validationState: null,
|
||||||
|
prefix: prefix ?? string.Empty,
|
||||||
|
model: model);
|
||||||
|
return ModelState.IsValid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,21 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Authentication;
|
using Microsoft.AspNetCore.Http.Authentication;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -21,6 +26,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
public abstract class PageModel
|
public abstract class PageModel
|
||||||
{
|
{
|
||||||
private PageArgumentBinder _binder;
|
private PageArgumentBinder _binder;
|
||||||
|
private IObjectModelValidator _objectValidator;
|
||||||
|
private IModelMetadataProvider _metadataProvider;
|
||||||
|
private IModelBinderFactory _modelBinderFactory;
|
||||||
private IUrlHelper _urlHelper;
|
private IUrlHelper _urlHelper;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -106,68 +114,405 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HttpResponse Response => HttpContext?.Response;
|
public HttpResponse Response => HttpContext?.Response;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="AspNetCore.Routing.RouteData"/> for the executing action.
|
||||||
|
/// </summary>
|
||||||
|
public RouteData RouteData => PageContext.RouteData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="ModelStateDictionary"/>.
|
/// Gets the <see cref="ModelStateDictionary"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ModelStateDictionary ModelState => PageContext.ModelState;
|
public ModelStateDictionary ModelState => PageContext.ModelState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="ClaimsPrincipal"/> for user associated with the executing action.
|
||||||
|
/// </summary>
|
||||||
|
public ClaimsPrincipal User => HttpContext?.User;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="ITempDataDictionary"/> from the <see cref="PageContext"/>.
|
/// Gets the <see cref="ITempDataDictionary"/> from the <see cref="PageContext"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Returns null if <see cref="PageContext"/> is null.</remarks>
|
/// <remarks>Returns null if <see cref="PageContext"/> is null.</remarks>
|
||||||
public ITempDataDictionary TempData => PageContext?.TempData;
|
public ITempDataDictionary TempData => PageContext?.TempData;
|
||||||
|
|
||||||
|
private IObjectModelValidator ObjectValidator
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_objectValidator == null)
|
||||||
|
{
|
||||||
|
_objectValidator = HttpContext?.RequestServices?.GetRequiredService<IObjectModelValidator>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _objectValidator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IModelMetadataProvider MetadataProvider
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_metadataProvider == null)
|
||||||
|
{
|
||||||
|
_metadataProvider = HttpContext?.RequestServices?.GetRequiredService<IModelMetadataProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _metadataProvider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IModelBinderFactory ModelBinderFactory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_modelBinderFactory == null)
|
||||||
|
{
|
||||||
|
_modelBinderFactory = HttpContext?.RequestServices?.GetRequiredService<IModelBinderFactory>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _modelBinderFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="ViewDataDictionary"/>.
|
/// Gets the <see cref="ViewDataDictionary"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ViewDataDictionary ViewData => PageContext?.ViewData;
|
public ViewDataDictionary ViewData => PageContext?.ViewData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Binds the model with the specified <paramref name="name"/>.
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="PageModel"/>'s current
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TModel">The model type.</typeparam>
|
|
||||||
/// <param name="name">The model name.</param>
|
|
||||||
/// <returns>A <see cref="Task"/> that on completion returns the bound model.</returns>
|
|
||||||
protected internal Task<TModel> BindAsync<TModel>(string name)
|
|
||||||
{
|
|
||||||
return Binder.BindModelAsync<TModel>(PageContext, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Binds the model with the specified <paramref name="name"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TModel">The model type.</typeparam>
|
|
||||||
/// <param name="name">The model name.</param>
|
|
||||||
/// <param name="default">The default model value.</param>
|
|
||||||
/// <returns>A <see cref="Task"/> that on completion returns the bound model.</returns>
|
|
||||||
protected internal Task<TModel> BindAsync<TModel>(TModel @default, string name)
|
|
||||||
{
|
|
||||||
return Binder.BindModelAsync(PageContext, @default, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the specified <paramref name="model"/> instance using values from the pageModel's current
|
|
||||||
/// <see cref="IValueProvider"/>.
|
/// <see cref="IValueProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
/// <param name="model">The model instance to update.</param>
|
/// <param name="model">The model instance to update.</param>
|
||||||
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
protected internal Task<bool> TryUpdateModelAsync<TModel>(TModel model)
|
protected internal Task<bool> TryUpdateModelAsync<TModel>(TModel model)
|
||||||
|
where TModel : class
|
||||||
{
|
{
|
||||||
return Binder.TryUpdateModelAsync(PageContext, model);
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryUpdateModelAsync(model, name: string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the specified <paramref name="model"/> instance using values from the pageModel's current
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="PageModel"/>'s current
|
||||||
/// <see cref="IValueProvider"/>.
|
/// <see cref="IValueProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
/// <param name="model">The model instance to update.</param>
|
/// <param name="model">The model instance to update.</param>
|
||||||
/// <param name="name">The model name.</param>
|
/// <param name="name">The model name.</param>
|
||||||
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
protected internal Task<bool> TryUpdateModelAsync<TModel>(TModel model, string name)
|
protected internal async Task<bool> TryUpdateModelAsync<TModel>(TModel model, string name)
|
||||||
|
where TModel : class
|
||||||
{
|
{
|
||||||
return Binder.TryUpdateModelAsync(PageContext, model, name);
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
|
||||||
|
return await TryUpdateModelAsync(model, name, valueProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the <paramref name="valueProvider"/> and a
|
||||||
|
/// <paramref name="name"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="name">The name to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
protected internal Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string name,
|
||||||
|
IValueProvider valueProvider)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(valueProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
name,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="PageModel"/>'s current
|
||||||
|
/// <see cref="IValueProvider"/> and a <paramref name="name"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="name">The name to use when looking up values in the current <see cref="IValueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="includeExpressions"> <see cref="Expression"/>(s) which represent top-level properties
|
||||||
|
/// which need to be included for the current model.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
protected internal async Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string name,
|
||||||
|
params Expression<Func<TModel, object>>[] includeExpressions)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeExpressions == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(includeExpressions));
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
|
||||||
|
return await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
name,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
includeExpressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="PageModel"/>'s current
|
||||||
|
/// <see cref="IValueProvider"/> and a <paramref name="name"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="name">The name to use when looking up values in the current <see cref="IValueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="propertyFilter">A predicate which can be used to filter properties at runtime.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
protected internal async Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string name,
|
||||||
|
Func<ModelMetadata, bool> propertyFilter)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyFilter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(propertyFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
|
||||||
|
return await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
name,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
propertyFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the <paramref name="valueProvider"/> and a
|
||||||
|
/// <paramref name="name"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="name">The name to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <param name="includeExpressions"> <see cref="Expression"/>(s) which represent top-level properties
|
||||||
|
/// which need to be included for the current model.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
protected internal Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string name,
|
||||||
|
IValueProvider valueProvider,
|
||||||
|
params Expression<Func<TModel, object>>[] includeExpressions)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(valueProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeExpressions == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(includeExpressions));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
name,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
includeExpressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the <paramref name="valueProvider"/> and a
|
||||||
|
/// <paramref name="name"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The type of the model object.</typeparam>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="name">The name to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <param name="propertyFilter">A predicate which can be used to filter properties at runtime.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
protected internal Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
|
TModel model,
|
||||||
|
string name,
|
||||||
|
IValueProvider valueProvider,
|
||||||
|
Func<ModelMetadata, bool> propertyFilter)
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(valueProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyFilter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(propertyFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
name,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
propertyFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="PageModel"/>'s current
|
||||||
|
/// <see cref="IValueProvider"/> and a <paramref name="name"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="modelType">The type of model instance to update.</param>
|
||||||
|
/// <param name="name">The name to use when looking up values in the current <see cref="IValueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
protected internal async Task<bool> TryUpdateModelAsync(
|
||||||
|
object model,
|
||||||
|
Type modelType,
|
||||||
|
string name)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(modelType));
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
|
||||||
|
return await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
modelType,
|
||||||
|
name,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the <paramref name="valueProvider"/> and a
|
||||||
|
/// <paramref name="name"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="modelType">The type of model instance to update.</param>
|
||||||
|
/// <param name="name">The name to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <param name="propertyFilter">A predicate which can be used to filter properties at runtime.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful.</returns>
|
||||||
|
protected internal Task<bool> TryUpdateModelAsync(
|
||||||
|
object model,
|
||||||
|
Type modelType,
|
||||||
|
string name,
|
||||||
|
IValueProvider valueProvider,
|
||||||
|
Func<ModelMetadata, bool> propertyFilter)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(modelType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(valueProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyFilter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(propertyFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
modelType,
|
||||||
|
name,
|
||||||
|
PageContext,
|
||||||
|
MetadataProvider,
|
||||||
|
ModelBinderFactory,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
propertyFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Factory methods
|
#region Factory methods
|
||||||
|
|
@ -1176,5 +1521,45 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
return new PageViewResult(Page);
|
return new PageViewResult(Page);
|
||||||
}
|
}
|
||||||
#endregion Factory methods
|
#endregion Factory methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the specified <paramref name="model"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model to validate.</param>
|
||||||
|
/// <returns><c>true</c> if the <see cref="ModelState"/> is valid; <c>false</c> otherwise.</returns>
|
||||||
|
public virtual bool TryValidateModel(
|
||||||
|
object model)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryValidateModel(model, name: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the specified <paramref name="model"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model to validate.</param>
|
||||||
|
/// <param name="name">The key to use when looking up information in <see cref="ModelState"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <returns><c>true</c> if the <see cref="ModelState"/> is valid;<c>false</c> otherwise.</returns>
|
||||||
|
public virtual bool TryValidateModel(
|
||||||
|
object model,
|
||||||
|
string name)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectValidator.Validate(
|
||||||
|
PageContext,
|
||||||
|
validationState: null,
|
||||||
|
prefix: name ?? string.Empty,
|
||||||
|
model: model);
|
||||||
|
return ModelState.IsValid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
|
{
|
||||||
|
public class RazorPageModelTest : IClassFixture<MvcTestFixture<RazorPagesWebSite.Startup>>
|
||||||
|
{
|
||||||
|
public RazorPageModelTest(MvcTestFixture<RazorPagesWebSite.Startup> fixture)
|
||||||
|
{
|
||||||
|
Client = fixture.Client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClient Client { get; }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Page_TryUpdateModelAsync_Success()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryUpdateModel/10")
|
||||||
|
{
|
||||||
|
Content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("Name", "Overriden"),
|
||||||
|
new KeyValuePair<string, string>("Age", "25"),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
await AddAntiforgeryHeaders(request);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Contains("Updated: True", content);
|
||||||
|
Assert.Contains("Name = Overriden", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Page_TryValidateModel_Success()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryValidateModel/10")
|
||||||
|
{
|
||||||
|
Content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("Name", "Foo"),
|
||||||
|
new KeyValuePair<string, string>("Age", "25"),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
await AddAntiforgeryHeaders(request);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
Assert.Contains("Validation: success", content.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Page_TryValidateModel_TooLong()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryValidateModel/10")
|
||||||
|
{
|
||||||
|
Content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("Name", "Foo"),
|
||||||
|
new KeyValuePair<string, string>("Age", "200"),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
await AddAntiforgeryHeaders(request);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
Assert.Contains("Validation: fail", content);
|
||||||
|
Assert.Contains("The field Age must be between 0 and 99.", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PageModel_TryUpdateModelAsync_Success()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryUpdateModelPageModel/10")
|
||||||
|
{
|
||||||
|
Content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("Name", "Overriden"),
|
||||||
|
new KeyValuePair<string, string>("Age", "25"),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
await AddAntiforgeryHeaders(request);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Contains("Updated: True", content);
|
||||||
|
Assert.Contains("Name = Overriden", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PageModel_TryValidateModel_Success()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryValidateModelPageModel/10")
|
||||||
|
{
|
||||||
|
Content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("Name", "Foo"),
|
||||||
|
new KeyValuePair<string, string>("Age", "25"),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
await AddAntiforgeryHeaders(request);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
Assert.Contains("Validation: success", content.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PageModel_TryValidateModel_TooLong()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryValidateModelPageModel/10")
|
||||||
|
{
|
||||||
|
Content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("Name", "Foo"),
|
||||||
|
new KeyValuePair<string, string>("Age", "200"),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
await AddAntiforgeryHeaders(request);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
Assert.Contains("Validation: fail", content);
|
||||||
|
Assert.Contains("The field Age must be between 0 and 99.", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddAntiforgeryHeaders(HttpRequestMessage request)
|
||||||
|
{
|
||||||
|
var getResponse = await Client.GetAsync(request.RequestUri);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode);
|
||||||
|
var getResponseBody = await getResponse.Content.ReadAsStringAsync();
|
||||||
|
var formToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(getResponseBody, "");
|
||||||
|
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse);
|
||||||
|
|
||||||
|
request.Headers.Add("Cookie", cookie.Key + "=" + cookie.Value);
|
||||||
|
request.Headers.Add("RequestVerificationToken", formToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1463,31 +1463,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
Assert.Same(urlHelper, pageModel.Url);
|
Assert.Same(urlHelper, pageModel.Url);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task BindModel_InvokesBindOnPageArgumentBinder()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var httpContext = new DefaultHttpContext();
|
|
||||||
var binder = new TestPageArgumentBinder();
|
|
||||||
httpContext.RequestServices = new ServiceCollection()
|
|
||||||
.AddSingleton<PageArgumentBinder>(binder)
|
|
||||||
.BuildServiceProvider();
|
|
||||||
var pageContext = new PageContext
|
|
||||||
{
|
|
||||||
HttpContext = httpContext,
|
|
||||||
};
|
|
||||||
var pageModel = new TestPageModel
|
|
||||||
{
|
|
||||||
PageContext = pageContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await pageModel.BindAsync<Guid>("test-name");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Redirect_ReturnsARedirectResult()
|
public void Redirect_ReturnsARedirectResult()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
@page "{id:int}"
|
||||||
|
@using RazorPagesWebSite
|
||||||
|
|
||||||
|
@functions
|
||||||
|
{
|
||||||
|
public UserModel UserModel { get; set; }
|
||||||
|
|
||||||
|
[FromRoute]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public async Task OnPost()
|
||||||
|
{
|
||||||
|
var user = new UserModel();
|
||||||
|
|
||||||
|
Updated = await TryUpdateModelAsync(user);
|
||||||
|
UserModel = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Updated { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Html.ValidationSummary()
|
||||||
|
<form action="">
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
</form>
|
||||||
|
Updated: @Updated
|
||||||
|
Name = @UserModel?.Name
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
|
namespace RazorPagesWebSite.Pages
|
||||||
|
{
|
||||||
|
public class TryUpdateModelPageModel : PageModel
|
||||||
|
{
|
||||||
|
public UserModel UserModel { get; set; }
|
||||||
|
|
||||||
|
public bool Updated { get; set; }
|
||||||
|
|
||||||
|
public async Task OnPost()
|
||||||
|
{
|
||||||
|
var user = new UserModel();
|
||||||
|
Updated = await TryUpdateModelAsync(user);
|
||||||
|
UserModel = user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
@page "{id:int}"
|
||||||
|
@using RazorPagesWebSite.Pages
|
||||||
|
@model TryUpdateModelPageModel
|
||||||
|
|
||||||
|
@Html.ValidationSummary()
|
||||||
|
<form action="">
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
</form>
|
||||||
|
Updated: @Model.Updated
|
||||||
|
Name = @Model.UserModel?.Name
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
@page "{id:int}"
|
||||||
|
@using RazorPagesWebSite
|
||||||
|
|
||||||
|
@functions
|
||||||
|
{
|
||||||
|
[ModelBinder]
|
||||||
|
public UserModel UserModel { get; set; }
|
||||||
|
|
||||||
|
[FromRoute]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public void OnPost(UserModel user)
|
||||||
|
{
|
||||||
|
Valid = TryValidateModel(user);
|
||||||
|
|
||||||
|
UserModel = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Valid { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Html.ValidationSummary()
|
||||||
|
<form action="">
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
</form>
|
||||||
|
Validation: @(Valid ? "success" : "fail!" )
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// 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.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
|
namespace RazorPagesWebSite.Pages
|
||||||
|
{
|
||||||
|
public class TryValidateModelPageModel : PageModel
|
||||||
|
{
|
||||||
|
[ModelBinder]
|
||||||
|
public UserModel UserModel { get; set; }
|
||||||
|
|
||||||
|
public bool Validate { get; set; }
|
||||||
|
|
||||||
|
public void OnPost(UserModel user)
|
||||||
|
{
|
||||||
|
Validate = TryValidateModel(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
@page "{id:int}"
|
||||||
|
@using RazorPagesWebSite.Pages
|
||||||
|
@model TryValidateModelPageModel
|
||||||
|
|
||||||
|
@Html.ValidationSummary()
|
||||||
|
<form action="">
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
</form>
|
||||||
|
Validation: @(Model.Validate ? "success" : "fail!" )
|
||||||
Loading…
Reference in New Issue