Add DeclaredModelType to Razor pages
* This allows razor pages to override their model type with a model that extends the declared model type through the page application model.
This commit is contained in:
parent
b30020a655
commit
7127bb5dbb
|
|
@ -24,14 +24,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
|
||||||
public RazorPagePropertyActivator(
|
public RazorPagePropertyActivator(
|
||||||
Type pageType,
|
Type pageType,
|
||||||
Type modelType,
|
Type declaredModelType,
|
||||||
IModelMetadataProvider metadataProvider,
|
IModelMetadataProvider metadataProvider,
|
||||||
PropertyValueAccessors propertyValueAccessors)
|
PropertyValueAccessors propertyValueAccessors)
|
||||||
{
|
{
|
||||||
_metadataProvider = metadataProvider;
|
_metadataProvider = metadataProvider;
|
||||||
|
|
||||||
// In the absence of a model on the current type, we'll attempt to use ViewDataDictionary<object> on the current type.
|
// In the absence of a model on the current type, we'll attempt to use ViewDataDictionary<object> on the current type.
|
||||||
var viewDataDictionaryModelType = modelType ?? typeof(object);
|
var viewDataDictionaryModelType = declaredModelType ?? typeof(object);
|
||||||
|
|
||||||
if (viewDataDictionaryModelType != null)
|
if (viewDataDictionaryModelType != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,21 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
PageActionDescriptor actionDescriptor,
|
PageActionDescriptor actionDescriptor,
|
||||||
TypeInfo handlerType,
|
TypeInfo handlerType,
|
||||||
IReadOnlyList<object> handlerAttributes)
|
IReadOnlyList<object> handlerAttributes)
|
||||||
|
: this(actionDescriptor, handlerType, handlerType, handlerAttributes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="PageApplicationModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
public PageApplicationModel(
|
||||||
|
PageActionDescriptor actionDescriptor,
|
||||||
|
TypeInfo declaredModelType,
|
||||||
|
TypeInfo handlerType,
|
||||||
|
IReadOnlyList<object> handlerAttributes)
|
||||||
{
|
{
|
||||||
ActionDescriptor = actionDescriptor ?? throw new ArgumentNullException(nameof(actionDescriptor));
|
ActionDescriptor = actionDescriptor ?? throw new ArgumentNullException(nameof(actionDescriptor));
|
||||||
|
DeclaredModelType = declaredModelType;
|
||||||
HandlerType = handlerType;
|
HandlerType = handlerType;
|
||||||
|
|
||||||
Filters = new List<IFilterMetadata>();
|
Filters = new List<IFilterMetadata>();
|
||||||
|
|
@ -56,7 +69,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
Properties = new Dictionary<object, object>(other.Properties);
|
Properties = new Dictionary<object, object>(other.Properties);
|
||||||
|
|
||||||
HandlerMethods = new List<PageHandlerModel>(other.HandlerMethods.Select(m => new PageHandlerModel(m)));
|
HandlerMethods = new List<PageHandlerModel>(other.HandlerMethods.Select(m => new PageHandlerModel(m)));
|
||||||
HandlerProperties = new List<PagePropertyModel>(other.HandlerProperties.Select(p => new PagePropertyModel(p)));
|
HandlerProperties = new List<PagePropertyModel>(other.HandlerProperties.Select(p => new PagePropertyModel(p)));
|
||||||
HandlerTypeAttributes = other.HandlerTypeAttributes;
|
HandlerTypeAttributes = other.HandlerTypeAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +122,16 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
public TypeInfo PageType { get; set; }
|
public TypeInfo PageType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="TypeInfo"/> of the Razor page model.
|
/// Gets the declared model <see cref="TypeInfo"/> of the model for the page.
|
||||||
|
/// Typically this <see cref="TypeInfo"/> will be the type specified by the @model directive
|
||||||
|
/// in the razor page.
|
||||||
|
/// </summary>
|
||||||
|
public TypeInfo DeclaredModelType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the runtime model <see cref="TypeInfo"/> of the model for the razor page.
|
||||||
|
/// This is the <see cref="TypeInfo"/> that will be used at runtime to instantiate and populate
|
||||||
|
/// the model property of the page.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TypeInfo ModelType { get; set; }
|
public TypeInfo ModelType { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
public TypeInfo HandlerTypeInfo { get; set; }
|
public TypeInfo HandlerTypeInfo { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="TypeInfo"/> of the model.
|
/// Gets or sets the declared model <see cref="TypeInfo"/> of the model for the page.
|
||||||
|
/// Typically this <see cref="TypeInfo"/> will be the type specified by the @model directive
|
||||||
|
/// in the razor page.
|
||||||
|
/// </summary>
|
||||||
|
public TypeInfo DeclaredModelTypeInfo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the runtime model <see cref="TypeInfo"/> of the model for the razor page.
|
||||||
|
/// This is the <see cref="TypeInfo"/> that will be used at runtime to instantiate and populate
|
||||||
|
/// the model property of the page.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TypeInfo ModelTypeInfo { get; set; }
|
public TypeInfo ModelTypeInfo { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
var activatorFactory = _pageActivator.CreateActivator(actionDescriptor);
|
var activatorFactory = _pageActivator.CreateActivator(actionDescriptor);
|
||||||
var modelType = actionDescriptor.ModelTypeInfo?.AsType() ?? actionDescriptor.PageTypeInfo.AsType();
|
var declaredModelType = actionDescriptor.DeclaredModelTypeInfo?.AsType() ?? actionDescriptor.PageTypeInfo.AsType();
|
||||||
var propertyActivator = new RazorPagePropertyActivator(
|
var propertyActivator = new RazorPagePropertyActivator(
|
||||||
actionDescriptor.PageTypeInfo.AsType(),
|
actionDescriptor.PageTypeInfo.AsType(),
|
||||||
modelType,
|
declaredModelType,
|
||||||
_modelMetadataProvider,
|
_modelMetadataProvider,
|
||||||
_propertyAccessors);
|
_propertyAccessors);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
|
@ -31,6 +32,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
.ToArray();
|
.ToArray();
|
||||||
var handlerMethods = CreateHandlerMethods(applicationModel);
|
var handlerMethods = CreateHandlerMethods(applicationModel);
|
||||||
|
|
||||||
|
if (applicationModel.ModelType != null && applicationModel.DeclaredModelType != null &&
|
||||||
|
!applicationModel.DeclaredModelType.IsAssignableFrom(applicationModel.ModelType))
|
||||||
|
{
|
||||||
|
var message = Resources.FormatInvalidActionDescriptorModelType(
|
||||||
|
applicationModel.ActionDescriptor.DisplayName,
|
||||||
|
applicationModel.ModelType.Name,
|
||||||
|
applicationModel.DeclaredModelType.Name);
|
||||||
|
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
var actionDescriptor = applicationModel.ActionDescriptor;
|
var actionDescriptor = applicationModel.ActionDescriptor;
|
||||||
return new CompiledPageActionDescriptor(actionDescriptor)
|
return new CompiledPageActionDescriptor(actionDescriptor)
|
||||||
{
|
{
|
||||||
|
|
@ -40,6 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
FilterDescriptors = filters,
|
FilterDescriptors = filters,
|
||||||
HandlerMethods = handlerMethods,
|
HandlerMethods = handlerMethods,
|
||||||
HandlerTypeInfo = applicationModel.HandlerType,
|
HandlerTypeInfo = applicationModel.HandlerType,
|
||||||
|
DeclaredModelTypeInfo = applicationModel.DeclaredModelType,
|
||||||
ModelTypeInfo = applicationModel.ModelType,
|
ModelTypeInfo = applicationModel.ModelType,
|
||||||
RouteValues = actionDescriptor.RouteValues,
|
RouteValues = actionDescriptor.RouteValues,
|
||||||
PageTypeInfo = applicationModel.PageType,
|
PageTypeInfo = applicationModel.PageType,
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelTypeInfo = modelProperty.PropertyType.GetTypeInfo();
|
var modelTypeInfo = modelProperty.PropertyType.GetTypeInfo();
|
||||||
|
var declaredModelType = modelTypeInfo;
|
||||||
|
|
||||||
// Now we want figure out which type is the handler type.
|
// Now we want figure out which type is the handler type.
|
||||||
TypeInfo handlerType;
|
TypeInfo handlerType;
|
||||||
|
|
@ -90,6 +91,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var handlerTypeAttributes = handlerType.GetCustomAttributes(inherit: true);
|
var handlerTypeAttributes = handlerType.GetCustomAttributes(inherit: true);
|
||||||
var pageModel = new PageApplicationModel(
|
var pageModel = new PageApplicationModel(
|
||||||
actionDescriptor,
|
actionDescriptor,
|
||||||
|
declaredModelType,
|
||||||
handlerType,
|
handlerType,
|
||||||
handlerTypeAttributes)
|
handlerTypeAttributes)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
var compiledActionDescriptor = (CompiledPageActionDescriptor)context.ActionContext.ActionDescriptor;
|
var compiledActionDescriptor = (CompiledPageActionDescriptor)context.ActionContext.ActionDescriptor;
|
||||||
|
|
||||||
var viewDataFactory = ViewDataDictionaryFactory.CreateFactory(compiledActionDescriptor.ModelTypeInfo);
|
var viewDataFactory = ViewDataDictionaryFactory.CreateFactory(compiledActionDescriptor.DeclaredModelTypeInfo);
|
||||||
|
|
||||||
var pageFactory = _pageFactoryProvider.CreatePageFactory(compiledActionDescriptor);
|
var pageFactory = _pageFactoryProvider.CreatePageFactory(compiledActionDescriptor);
|
||||||
var pageDisposer = _pageFactoryProvider.CreatePageDisposer(compiledActionDescriptor);
|
var pageDisposer = _pageFactoryProvider.CreatePageDisposer(compiledActionDescriptor);
|
||||||
|
|
|
||||||
|
|
@ -10,20 +10,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
private static readonly ResourceManager _resourceManager
|
private static readonly ResourceManager _resourceManager
|
||||||
= new ResourceManager("Microsoft.AspNetCore.Mvc.RazorPages.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
= new ResourceManager("Microsoft.AspNetCore.Mvc.RazorPages.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The route for the page at '{0}' cannot start with / or ~/. Pages do not support overriding the file path of the page.
|
|
||||||
/// </summary>
|
|
||||||
internal static string PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable
|
|
||||||
{
|
|
||||||
get => GetString("PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The route for the page at '{0}' cannot start with / or ~/. Pages do not support overriding the file path of the page.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatPageActionDescriptorProvider_RouteTemplateCannotBeOverrideable(object p0)
|
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable"), p0);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The '{0}' property of '{1}' must not be null.
|
/// The '{0}' property of '{1}' must not be null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -178,6 +164,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
internal static string FormatInvalidValidPageName(object p0)
|
internal static string FormatInvalidValidPageName(object p0)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidValidPageName"), p0);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidValidPageName"), p0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model type for '{0}' is of type '{1}' which is not assignable to its declared model type '{2}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidActionDescriptorModelType
|
||||||
|
{
|
||||||
|
get => GetString("InvalidActionDescriptorModelType");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model type for '{0}' is of type '{1}' which is not assignable to its declared model type '{2}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidActionDescriptorModelType(object p0, object p1, object p2)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidActionDescriptorModelType"), p0, p1, p2);
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
||||||
|
|
@ -150,4 +150,7 @@
|
||||||
<data name="InvalidValidPageName" xml:space="preserve">
|
<data name="InvalidValidPageName" xml:space="preserve">
|
||||||
<value>'{0}' is not a valid page name. A page name is path relative to the Razor Pages root directory that starts with a leading forward slash ('/') and does not contain the file extension e.g "/Users/Edit".</value>
|
<value>'{0}' is not a valid page name. A page name is path relative to the Razor Pages root directory that starts with a leading forward slash ('/') and does not contain the file extension e.g "/Users/Edit".</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="InvalidActionDescriptorModelType" xml:space="preserve">
|
||||||
|
<value>The model type for '{0}' is of type '{1}' which is not assignable to its declared model type '{2}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -114,7 +115,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal("/Login?ReturnUrl=%2FConventions%2FAuthFolder", response.Headers.Location.PathAndQuery);
|
Assert.Equal("/Login?ReturnUrl=%2FConventions%2FAuthFolder", response.Headers.Location.PathAndQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AuthConvention_AppliedToFolders_CanByOverridenByFiltersOnModel()
|
public async Task AuthConvention_AppliedToFolders_CanByOverridenByFiltersOnModel()
|
||||||
{
|
{
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -391,5 +392,76 @@ Hello from /Pages/Shared/";
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("Hello from AllowAnonymous", response.Trim());
|
Assert.Equal("Hello from AllowAnonymous", response.Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These test is important as it covers a feature that allows razor pages to use a different
|
||||||
|
// model at runtime that wasn't known at compile time. Like a non-generic model used at compile
|
||||||
|
// time and overrided at runtime with a closed-generic model that performs the actual implementation.
|
||||||
|
// An example of this is how the Identity UI library defines a base page model in their views,
|
||||||
|
// like how the Register.cshtml view defines its model as RegisterModel and then, at runtime it replaces
|
||||||
|
// that model with RegisterModel<TUser> where TUser is the type of the user used to configure identity.
|
||||||
|
[Fact]
|
||||||
|
public async Task PageConventions_CanBeUsedToCustomizeTheModelType()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetAsync("/CustomModelTypeModel");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Contains("<h2>User</h2>", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PageConventions_CustomizedModelCanPostToHandlers()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var getPage = await Client.GetAsync("/CustomModelTypeModel");
|
||||||
|
var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(await getPage.Content.ReadAsStringAsync(), "");
|
||||||
|
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getPage);
|
||||||
|
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel");
|
||||||
|
message.Content = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["__RequestVerificationToken"] = token,
|
||||||
|
["ConfirmPassword"] = "",
|
||||||
|
["Password"] = "",
|
||||||
|
["Email"] = ""
|
||||||
|
});
|
||||||
|
message.Headers.TryAddWithoutValidation("Cookie", $"{cookie.Key}={cookie.Value}");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Contains("is required.", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PageConventions_CustomizedModelCanWorkWithModelState()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var getPage = await Client.GetAsync("/CustomModelTypeModel");
|
||||||
|
var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(await getPage.Content.ReadAsStringAsync(), "");
|
||||||
|
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getPage);
|
||||||
|
|
||||||
|
var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel");
|
||||||
|
message.Content = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["__RequestVerificationToken"] = token,
|
||||||
|
["Email"] = "javi@example.com",
|
||||||
|
["Password"] = "Password.12$",
|
||||||
|
["ConfirmPassword"] = "Password.12$",
|
||||||
|
});
|
||||||
|
message.Headers.TryAddWithoutValidation("Cookie", $"{cookie.Key}={cookie.Value}");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||||
|
Assert.Equal("/", response.Headers.Location.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
var activator = new RazorPagePropertyActivator(
|
var activator = new RazorPagePropertyActivator(
|
||||||
typeof(TestPage),
|
typeof(TestPage),
|
||||||
modelType: null,
|
declaredModelType: null,
|
||||||
metadataProvider: new TestModelMetadataProvider(),
|
metadataProvider: new TestModelMetadataProvider(),
|
||||||
propertyValueAccessors: null);
|
propertyValueAccessors: null);
|
||||||
var viewContext = new ViewContext();
|
var viewContext = new ViewContext();
|
||||||
|
|
@ -55,7 +55,36 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
var modelMetadataProvider = new TestModelMetadataProvider();
|
var modelMetadataProvider = new TestModelMetadataProvider();
|
||||||
var activator = new RazorPagePropertyActivator(
|
var activator = new RazorPagePropertyActivator(
|
||||||
typeof(TestPage),
|
typeof(TestPage),
|
||||||
modelType: typeof(TestModel),
|
declaredModelType: typeof(TestModel),
|
||||||
|
metadataProvider: modelMetadataProvider,
|
||||||
|
propertyValueAccessors: null);
|
||||||
|
var original = new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary())
|
||||||
|
{
|
||||||
|
{ "test-key", "test-value" },
|
||||||
|
};
|
||||||
|
var viewContext = new ViewContext
|
||||||
|
{
|
||||||
|
ViewData = original,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var viewDataDictionary = activator.CreateViewDataDictionary(viewContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(viewDataDictionary);
|
||||||
|
Assert.NotSame(original, viewDataDictionary);
|
||||||
|
Assert.IsType<ViewDataDictionary<TestModel>>(viewDataDictionary);
|
||||||
|
Assert.Equal("test-value", viewDataDictionary["test-key"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateViewDataDictionary_UsesDeclaredTypeOverModelType_WhenCreatingTheViewDataDictionary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var modelMetadataProvider = new TestModelMetadataProvider();
|
||||||
|
var activator = new RazorPagePropertyActivator(
|
||||||
|
typeof(TestPage),
|
||||||
|
declaredModelType: typeof(TestModel),
|
||||||
metadataProvider: modelMetadataProvider,
|
metadataProvider: modelMetadataProvider,
|
||||||
propertyValueAccessors: null);
|
propertyValueAccessors: null);
|
||||||
var original = new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary())
|
var original = new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary())
|
||||||
|
|
@ -84,7 +113,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
var modelMetadataProvider = new TestModelMetadataProvider();
|
var modelMetadataProvider = new TestModelMetadataProvider();
|
||||||
var activator = new RazorPagePropertyActivator(
|
var activator = new RazorPagePropertyActivator(
|
||||||
typeof(TestPage),
|
typeof(TestPage),
|
||||||
modelType: typeof(TestModel),
|
declaredModelType: typeof(TestModel),
|
||||||
metadataProvider: modelMetadataProvider,
|
metadataProvider: modelMetadataProvider,
|
||||||
propertyValueAccessors: null);
|
propertyValueAccessors: null);
|
||||||
var original = new ViewDataDictionary<object>(modelMetadataProvider, new ModelStateDictionary())
|
var original = new ViewDataDictionary<object>(modelMetadataProvider, new ModelStateDictionary())
|
||||||
|
|
@ -113,7 +142,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
var modelMetadataProvider = new TestModelMetadataProvider();
|
var modelMetadataProvider = new TestModelMetadataProvider();
|
||||||
var activator = new RazorPagePropertyActivator(
|
var activator = new RazorPagePropertyActivator(
|
||||||
typeof(TestPage),
|
typeof(TestPage),
|
||||||
modelType: null,
|
declaredModelType: null,
|
||||||
metadataProvider: modelMetadataProvider,
|
metadataProvider: modelMetadataProvider,
|
||||||
propertyValueAccessors: null);
|
propertyValueAccessors: null);
|
||||||
var original = new ViewDataDictionary<TestModel>(modelMetadataProvider, new ModelStateDictionary())
|
var original = new ViewDataDictionary<TestModel>(modelMetadataProvider, new ModelStateDictionary())
|
||||||
|
|
@ -142,7 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
var modelMetadataProvider = new TestModelMetadataProvider();
|
var modelMetadataProvider = new TestModelMetadataProvider();
|
||||||
var activator = new RazorPagePropertyActivator(
|
var activator = new RazorPagePropertyActivator(
|
||||||
typeof(TestPage),
|
typeof(TestPage),
|
||||||
modelType: typeof(TestModel),
|
declaredModelType: typeof(TestModel),
|
||||||
metadataProvider: modelMetadataProvider,
|
metadataProvider: modelMetadataProvider,
|
||||||
propertyValueAccessors: null);
|
propertyValueAccessors: null);
|
||||||
var original = new ViewDataDictionary<TestModel>(modelMetadataProvider, new ModelStateDictionary())
|
var original = new ViewDataDictionary<TestModel>(modelMetadataProvider, new ModelStateDictionary())
|
||||||
|
|
@ -169,7 +198,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
var modelMetadataProvider = new TestModelMetadataProvider();
|
var modelMetadataProvider = new TestModelMetadataProvider();
|
||||||
var activator = new RazorPagePropertyActivator(
|
var activator = new RazorPagePropertyActivator(
|
||||||
typeof(TestPage),
|
typeof(TestPage),
|
||||||
modelType: null,
|
declaredModelType: null,
|
||||||
metadataProvider: modelMetadataProvider,
|
metadataProvider: modelMetadataProvider,
|
||||||
propertyValueAccessors: null);
|
propertyValueAccessors: null);
|
||||||
var original = new ViewDataDictionary<object>(modelMetadataProvider, new ModelStateDictionary());
|
var original = new ViewDataDictionary<object>(modelMetadataProvider, new ModelStateDictionary());
|
||||||
|
|
@ -193,5 +222,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
private class TestModel
|
private class TestModel
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DerivedTestModel : TestModel
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
var descriptor = new CompiledPageActionDescriptor
|
var descriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
PageTypeInfo = typeof(ViewDataTestPage).GetTypeInfo(),
|
PageTypeInfo = typeof(ViewDataTestPage).GetTypeInfo(),
|
||||||
|
DeclaredModelTypeInfo = typeof(ViewDataTestPageModel).GetTypeInfo(),
|
||||||
ModelTypeInfo = typeof(ViewDataTestPageModel).GetTypeInfo()
|
ModelTypeInfo = typeof(ViewDataTestPageModel).GetTypeInfo()
|
||||||
};
|
};
|
||||||
descriptor.RelativePath = "/this/is/a/path.cshtml";
|
descriptor.RelativePath = "/this/is/a/path.cshtml";
|
||||||
|
|
@ -139,6 +140,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
ActionDescriptor = new CompiledPageActionDescriptor
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
{
|
{
|
||||||
PageTypeInfo = typeof(ViewDataTestPage).GetTypeInfo(),
|
PageTypeInfo = typeof(ViewDataTestPage).GetTypeInfo(),
|
||||||
|
DeclaredModelTypeInfo = typeof(ViewDataTestPageModel).GetTypeInfo(),
|
||||||
ModelTypeInfo = typeof(ViewDataTestPageModel).GetTypeInfo(),
|
ModelTypeInfo = typeof(ViewDataTestPageModel).GetTypeInfo(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -156,6 +158,33 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
Assert.NotNull(testPage.ViewData);
|
Assert.NotNull(testPage.ViewData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PageFactorySetViewDataWithDeclaredModelTypeWhenNotNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var pageContext = new PageContext
|
||||||
|
{
|
||||||
|
ActionDescriptor = new CompiledPageActionDescriptor
|
||||||
|
{
|
||||||
|
PageTypeInfo = typeof(ViewDataTestPage).GetTypeInfo(),
|
||||||
|
DeclaredModelTypeInfo = typeof(ViewDataTestPageModel).GetTypeInfo(),
|
||||||
|
ModelTypeInfo = typeof(DerivedViewDataTestPageModel).GetTypeInfo(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var viewContext = new ViewContext();
|
||||||
|
|
||||||
|
var factoryProvider = CreatePageFactory();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var factory = factoryProvider.CreatePageFactory(pageContext.ActionDescriptor);
|
||||||
|
var instance = factory(pageContext, viewContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var testPage = Assert.IsType<ViewDataTestPage>(instance);
|
||||||
|
Assert.NotNull(testPage.ViewData);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PageFactorySetsNonGenericViewDataDictionary()
|
public void PageFactorySetsNonGenericViewDataDictionary()
|
||||||
{
|
{
|
||||||
|
|
@ -334,6 +363,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DerivedViewDataTestPageModel : ViewDataTestPageModel
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private class PropertiesWithoutRazorInject : Page
|
private class PropertiesWithoutRazorInject : Page
|
||||||
{
|
{
|
||||||
public IModelExpressionProvider ModelExpressionProviderWithoutInject { get; set; }
|
public IModelExpressionProvider ModelExpressionProviderWithoutInject { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
@ -58,7 +59,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
ViewEnginePath = "/Pages/Foo",
|
ViewEnginePath = "/Pages/Foo",
|
||||||
};
|
};
|
||||||
var handlerTypeInfo = typeof(TestModel).GetTypeInfo();
|
var handlerTypeInfo = typeof(TestModel).GetTypeInfo();
|
||||||
var pageApplicationModel = new PageApplicationModel(actionDescriptor, handlerTypeInfo, new object[0])
|
var pageApplicationModel = new PageApplicationModel(actionDescriptor, typeof(TestModel).GetTypeInfo(), handlerTypeInfo, new object[0])
|
||||||
{
|
{
|
||||||
PageType = typeof(TestPage).GetTypeInfo(),
|
PageType = typeof(TestPage).GetTypeInfo(),
|
||||||
ModelType = typeof(TestModel).GetTypeInfo(),
|
ModelType = typeof(TestModel).GetTypeInfo(),
|
||||||
|
|
@ -86,6 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(pageApplicationModel.PageType, actual.PageTypeInfo);
|
Assert.Same(pageApplicationModel.PageType, actual.PageTypeInfo);
|
||||||
|
Assert.Same(pageApplicationModel.DeclaredModelType, actual.DeclaredModelTypeInfo);
|
||||||
Assert.Same(pageApplicationModel.ModelType, actual.ModelTypeInfo);
|
Assert.Same(pageApplicationModel.ModelType, actual.ModelTypeInfo);
|
||||||
Assert.Same(pageApplicationModel.HandlerType, actual.HandlerTypeInfo);
|
Assert.Same(pageApplicationModel.HandlerType, actual.HandlerTypeInfo);
|
||||||
Assert.Same(pageApplicationModel.Properties, actual.Properties);
|
Assert.Same(pageApplicationModel.Properties, actual.Properties);
|
||||||
|
|
@ -94,6 +96,48 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.Equal(pageApplicationModel.HandlerProperties.Select(p => p.PropertyName), actual.BoundProperties.Select(p => p.Name));
|
Assert.Equal(pageApplicationModel.HandlerProperties.Select(p => p.PropertyName), actual.BoundProperties.Select(p => p.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateDescriptor_ThrowsIfModelIsNotCompatibleWithDeclaredModel()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actionDescriptor = new PageActionDescriptor
|
||||||
|
{
|
||||||
|
ActionConstraints = new List<IActionConstraintMetadata>(),
|
||||||
|
AttributeRouteInfo = new AttributeRouteInfo(),
|
||||||
|
FilterDescriptors = new List<FilterDescriptor>(),
|
||||||
|
RelativePath = "/Foo",
|
||||||
|
RouteValues = new Dictionary<string, string>(),
|
||||||
|
ViewEnginePath = "/Pages/Foo",
|
||||||
|
};
|
||||||
|
var handlerTypeInfo = typeof(TestModel).GetTypeInfo();
|
||||||
|
var pageApplicationModel = new PageApplicationModel(actionDescriptor, typeof(TestModel).GetTypeInfo(), handlerTypeInfo, new object[0])
|
||||||
|
{
|
||||||
|
PageType = typeof(TestPage).GetTypeInfo(),
|
||||||
|
ModelType = typeof(string).GetTypeInfo(),
|
||||||
|
Filters =
|
||||||
|
{
|
||||||
|
Mock.Of<IFilterMetadata>(),
|
||||||
|
Mock.Of<IFilterMetadata>(),
|
||||||
|
},
|
||||||
|
HandlerMethods =
|
||||||
|
{
|
||||||
|
new PageHandlerModel(handlerTypeInfo.GetMethod(nameof(TestModel.OnGet)), new object[0]),
|
||||||
|
},
|
||||||
|
HandlerProperties =
|
||||||
|
{
|
||||||
|
new PagePropertyModel(handlerTypeInfo.GetProperty(nameof(TestModel.Property)), new object[0])
|
||||||
|
{
|
||||||
|
BindingInfo = new BindingInfo(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var globalFilters = new FilterCollection();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var actual = Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
CompiledPageActionDescriptorBuilder.Build(pageApplicationModel, globalFilters));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CreateDescriptor_AddsGlobalFiltersWithTheRightScope()
|
public void CreateDescriptor_AddsGlobalFiltersWithTheRightScope()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -470,8 +470,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var pageModel = context.PageApplicationModel;
|
var pageModel = context.PageApplicationModel;
|
||||||
Assert.Empty(pageModel.HandlerProperties.Where(p => p.BindingInfo != null));
|
Assert.Empty(pageModel.HandlerProperties.Where(p => p.BindingInfo != null));
|
||||||
Assert.Empty(pageModel.HandlerMethods);
|
Assert.Empty(pageModel.HandlerMethods);
|
||||||
Assert.Same(typeof(EmptyPageModel).GetTypeInfo(), pageModel.HandlerType);
|
Assert.Same(typeof(EmptyPageModel).GetTypeInfo(), pageModel.DeclaredModelType);
|
||||||
Assert.Same(typeof(EmptyPageModel).GetTypeInfo(), pageModel.ModelType);
|
Assert.Same(typeof(EmptyPageModel).GetTypeInfo(), pageModel.ModelType);
|
||||||
|
Assert.Same(typeof(EmptyPageModel).GetTypeInfo(), pageModel.HandlerType);
|
||||||
Assert.Same(typeof(EmptyPageWithPageModel).GetTypeInfo(), pageModel.PageType);
|
Assert.Same(typeof(EmptyPageWithPageModel).GetTypeInfo(), pageModel.PageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var descriptor = new PageActionDescriptor
|
var descriptor = new PageActionDescriptor
|
||||||
{
|
{
|
||||||
RelativePath = "Path1",
|
RelativePath = "Path1",
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
Func<PageContext, ViewContext, object> factory = (a, b) => null;
|
Func<PageContext, ViewContext, object> factory = (a, b) => null;
|
||||||
|
|
@ -102,7 +102,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader
|
loader
|
||||||
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel)));
|
.Returns(CreateCompiledPageActionDescriptor(
|
||||||
|
descriptor,
|
||||||
|
pageType: typeof(PageWithModel),
|
||||||
|
modelType: typeof(DerivedTestPageModel)));
|
||||||
|
|
||||||
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
||||||
pageFactoryProvider
|
pageFactoryProvider
|
||||||
|
|
@ -322,7 +325,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetViewStartFactories_FindsFullHeirarchy()
|
public void GetViewStartFactories_FindsFullHierarchy()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -437,20 +440,27 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
private static CompiledPageActionDescriptor CreateCompiledPageActionDescriptor(
|
private static CompiledPageActionDescriptor CreateCompiledPageActionDescriptor(
|
||||||
PageActionDescriptor descriptor,
|
PageActionDescriptor descriptor,
|
||||||
Type pageType = null)
|
Type pageType = null,
|
||||||
|
Type modelType = null)
|
||||||
{
|
{
|
||||||
pageType = pageType ?? typeof(object);
|
pageType = pageType ?? typeof(object);
|
||||||
var pageTypeInfo = pageType.GetTypeInfo();
|
var pageTypeInfo = pageType.GetTypeInfo();
|
||||||
|
|
||||||
TypeInfo modelTypeInfo = null;
|
var modelTypeInfo = modelType?.GetTypeInfo();
|
||||||
|
TypeInfo declaredModelTypeInfo = null;
|
||||||
if (pageType != null)
|
if (pageType != null)
|
||||||
{
|
{
|
||||||
modelTypeInfo = pageTypeInfo.GetProperty("Model")?.PropertyType.GetTypeInfo();
|
declaredModelTypeInfo = pageTypeInfo.GetProperty("Model")?.PropertyType.GetTypeInfo();
|
||||||
|
if (modelTypeInfo == null)
|
||||||
|
{
|
||||||
|
modelTypeInfo = declaredModelTypeInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CompiledPageActionDescriptor(descriptor)
|
return new CompiledPageActionDescriptor(descriptor)
|
||||||
{
|
{
|
||||||
HandlerTypeInfo = modelTypeInfo ?? pageTypeInfo,
|
HandlerTypeInfo = modelTypeInfo ?? pageTypeInfo,
|
||||||
|
DeclaredModelTypeInfo = declaredModelTypeInfo ?? pageTypeInfo,
|
||||||
ModelTypeInfo = modelTypeInfo ?? pageTypeInfo,
|
ModelTypeInfo = modelTypeInfo ?? pageTypeInfo,
|
||||||
PageTypeInfo = pageTypeInfo,
|
PageTypeInfo = pageTypeInfo,
|
||||||
FilterDescriptors = Array.Empty<FilterDescriptor>(),
|
FilterDescriptors = Array.Empty<FilterDescriptor>(),
|
||||||
|
|
@ -522,5 +532,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DerivedTestPageModel : TestPageModel
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// 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.Mvc.ApplicationModels;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace RazorPagesWebSite.Conventions
|
||||||
|
{
|
||||||
|
internal class CustomModelTypeConvention : IPageApplicationModelConvention
|
||||||
|
{
|
||||||
|
public void Apply(PageApplicationModel model)
|
||||||
|
{
|
||||||
|
if (model.ModelType == typeof(CustomModelTypeModel))
|
||||||
|
{
|
||||||
|
model.ModelType = typeof(CustomModelTypeModel<User>).GetTypeInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
@page
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@using RazorPagesWebSite
|
||||||
|
@model CustomModelTypeModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Register";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"]</h2>
|
||||||
|
<h2>@ViewData["UserType"]</h2>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<form asp-route-returnUrl="@Model.ReturnUrl" method="post">
|
||||||
|
<h4>Create a new account.</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.Email"></label>
|
||||||
|
<input asp-for="Input.Email" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.Password"></label>
|
||||||
|
<input asp-for="Input.Password" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.ConfirmPassword"></label>
|
||||||
|
<input asp-for="Input.ConfirmPassword" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-default">Register</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace RazorPagesWebSite
|
||||||
|
{
|
||||||
|
public class CustomModelTypeModel : PageModel
|
||||||
|
{
|
||||||
|
[BindProperty]
|
||||||
|
public InputModel Input { get; set; }
|
||||||
|
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
|
||||||
|
public class InputModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Password")]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirm password")]
|
||||||
|
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||||
|
public string ConfirmPassword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnGet(string returnUrl = null)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IActionResult OnPostAsync(string returnUrl = null)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CustomModelTypeModel<TUser> : CustomModelTypeModel where TUser : User
|
||||||
|
{
|
||||||
|
private readonly ILogger<CustomModelTypeModel<TUser>> _logger;
|
||||||
|
|
||||||
|
public CustomModelTypeModel(ILogger<CustomModelTypeModel<TUser>> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGet(string returnUrl = null)
|
||||||
|
{
|
||||||
|
// We only care about being able to resolve the service from DI.
|
||||||
|
// The line below is just to make the compiler happy.
|
||||||
|
_logger.LogInformation(typeof(TUser).Name);
|
||||||
|
ViewData["UserType"] = typeof(TUser).Name;
|
||||||
|
ReturnUrl = returnUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IActionResult OnPostAsync(string returnUrl = null)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Redirect("~/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
// 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 System.Globalization;
|
using System.Globalization;
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using RazorPagesWebSite.Conventions;
|
||||||
|
|
||||||
namespace RazorPagesWebSite
|
namespace RazorPagesWebSite
|
||||||
{
|
{
|
||||||
|
|
@ -22,6 +23,7 @@ namespace RazorPagesWebSite
|
||||||
options.Conventions.AllowAnonymousToPage("/Pages/Admin/Login");
|
options.Conventions.AllowAnonymousToPage("/Pages/Admin/Login");
|
||||||
options.Conventions.AddPageRoute("/HelloWorldWithRoute", "Different-Route/{text}");
|
options.Conventions.AddPageRoute("/HelloWorldWithRoute", "Different-Route/{text}");
|
||||||
options.Conventions.AddPageRoute("/Pages/NotTheRoot", string.Empty);
|
options.Conventions.AddPageRoute("/Pages/NotTheRoot", string.Empty);
|
||||||
|
options.Conventions.Add(new CustomModelTypeConvention());
|
||||||
})
|
})
|
||||||
.WithRazorPagesAtContentRoot();
|
.WithRazorPagesAtContentRoot();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using RazorPagesWebSite.Conventions;
|
||||||
|
|
||||||
namespace RazorPagesWebSite
|
namespace RazorPagesWebSite
|
||||||
{
|
{
|
||||||
|
|
@ -22,6 +23,7 @@ namespace RazorPagesWebSite
|
||||||
options.Conventions.AuthorizeFolder("/Conventions/AuthFolder");
|
options.Conventions.AuthorizeFolder("/Conventions/AuthFolder");
|
||||||
options.Conventions.AuthorizeAreaFolder("Accounts", "/RequiresAuth");
|
options.Conventions.AuthorizeAreaFolder("Accounts", "/RequiresAuth");
|
||||||
options.Conventions.AllowAnonymousToAreaPage("Accounts", "/RequiresAuth/AllowAnonymous");
|
options.Conventions.AllowAnonymousToAreaPage("Accounts", "/RequiresAuth/AllowAnonymous");
|
||||||
|
options.Conventions.Add(new CustomModelTypeConvention());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue