Merge pull request #15355 from aspnet/prkrishn/merge-release

Merge release/3.1
This commit is contained in:
Pranav K 2019-10-24 11:03:23 -07:00 committed by GitHub
commit ec8304ae85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
143 changed files with 3781 additions and 916 deletions

2
.github/CODEOWNERS vendored
View File

@ -19,4 +19,4 @@
/src/Servers/ @tratcher @jkotalik @anurse @halter73
/src/Middleware/Rewrite @jkotalik @anurse
/src/Middleware/HttpsPolicy @jkotalik @anurse
/src/SignalR/ @mikaelm12 @BrennanConroy @halter73 @anurse
/src/SignalR/ @BrennanConroy @halter73 @anurse

View File

@ -15,6 +15,7 @@
<ProjectReferenceProvider Include="GetDocument.Insider" ProjectPath="$(RepoRoot)src\Tools\GetDocumentInsider\src\GetDocumentInsider.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Specification.Tests" ProjectPath="$(RepoRoot)src\SignalR\server\Specification.Tests\src\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.Build" ProjectPath="$(RepoRoot)src\Components\Blazor\Build\src\Microsoft.AspNetCore.Blazor.Build.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation" ProjectPath="$(RepoRoot)src\Components\Blazor\Validation\src\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj" />
<ProjectReferenceProvider Include="Ignitor" ProjectPath="$(RepoRoot)src\Components\Ignitor\src\Ignitor.csproj" />
<ProjectReferenceProvider Include="BlazorServerApp" ProjectPath="$(RepoRoot)src\Components\Samples\BlazorServerApp\BlazorServerApp.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore" ProjectPath="$(RepoRoot)src\DefaultBuilder\src\Microsoft.AspNetCore.csproj" RefProjectPath="$(RepoRoot)src\DefaultBuilder\ref\Microsoft.AspNetCore.csproj" />

View File

@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
@ -89,19 +91,26 @@ namespace Microsoft.AspNetCore.Builder
}
catch (Exception ex)
{
await context.Response.WriteAsync($@"
<h1>Unable to find debuggable browser tab</h1>
<p>
<h1>Unable to find debuggable browser tab</h1>
<p>
Could not get a list of browser tabs from <code>{debuggerTabsListUrl}</code>.
Ensure Chrome is running with debugging enabled.
</p>
<h2>Resolution</h2>
Ensure your browser is running with debugging enabled.
</p>
<h2>Resolution</h2>
<p>
<h4>If you are using Google Chrome for your development, follow these instructions:</h4>
{GetLaunchChromeInstructions(appRootUrl)}
<p>... then use that new tab for debugging.</p>
<h2>Underlying exception:</h2>
<pre>{ex}</pre>
");
</p>
<p>
<h4>If you are using Microsoft Edge (Chromium) for your development, follow these instructions:</h4>
{GetLaunchEdgeInstructions(appRootUrl)}
</p>
<strong>This should launch a new browser window with debugging enabled..</p>
<h2>Underlying exception:</h2>
<pre>{ex}</pre>
");
return;
}
@ -144,20 +153,42 @@ namespace Microsoft.AspNetCore.Builder
private static string GetLaunchChromeInstructions(string appRootUrl)
{
var profilePath = Path.Combine(Path.GetTempPath(), "blazor-edge-debug");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return $@"<p>Close all Chrome instances, then press Win+R and enter the following:</p>
<p><strong><code>""%programfiles(x86)%\Google\Chrome\Application\chrome.exe"" --remote-debugging-port=9222 {appRootUrl}</code></strong></p>";
return $@"<p>Press Win+R and enter the following:</p>
<p><strong><code>chrome --remote-debugging-port=9222 --user-data-dir=""{profilePath}"" {appRootUrl}</code></strong></p>";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return $@"<p>Close all Chrome instances, then in a terminal window execute the following:</p>
<p><strong><code>google-chrome --remote-debugging-port=9222 {appRootUrl}</code></strong></p>";
return $@"<p>In a terminal window execute the following:</p>
<p><strong><code>google-chrome --remote-debugging-port=9222 --user-data-dir={profilePath} {appRootUrl}</code></strong></p>";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return $@"<p>Close all Chrome instances, then in a terminal window execute the following:</p>
<p><strong><code>open /Applications/Google\ Chrome.app --args --remote-debugging-port=9222 {appRootUrl}</code></strong></p>";
return $@"<p>Execute the following:</p>
<p><strong><code>open /Applications/Google\ Chrome.app --args --remote-debugging-port=9222 --user-data-dir={profilePath} {appRootUrl}</code></strong></p>";
}
else
{
throw new InvalidOperationException("Unknown OS platform");
}
}
private static string GetLaunchEdgeInstructions(string appRootUrl)
{
var profilePath = Path.Combine(Path.GetTempPath(), "blazor-chrome-debug");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return $@"<p>Press Win+R and enter the following:</p>
<p><strong><code>msedge --remote-debugging-port=9222 --user-data-dir=""{profilePath}"" {appRootUrl}</code></strong></p>";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return $@"<p>In a terminal window execute the following:</p>
<p><strong><code>open /Applications/Microsoft\ Edge\ Dev.app --args --remote-debugging-port=9222 --user-data-dir={profilePath} {appRootUrl}</code></strong></p>";
}
else
{

View File

@ -4,7 +4,7 @@
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=2100553">brief survey</a>
<a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=2109206">brief survey</a>
</span>
and tell us what you think.
</div>

View File

@ -4,6 +4,16 @@ html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a, .btn-link {
color: #0366d6;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
app {
position: relative;
display: flex;
@ -21,10 +31,21 @@ app {
}
.main .top-row {
background-color: #e6e6e6;
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
}
.main .top-row > a, .main .top-row .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
}
.main .top-row a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
@ -44,20 +65,20 @@ app {
top: -2px;
}
.nav-item {
.sidebar .nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
}
.nav-item:first-of-type {
.sidebar .nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
.sidebar .nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item a {
.sidebar .nav-item a {
color: #d7d7d7;
border-radius: 4px;
height: 3rem;
@ -66,12 +87,12 @@ app {
line-height: 3rem;
}
.nav-item a.active {
.sidebar .nav-item a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
.nav-item a:hover {
.sidebar .nav-item a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
@ -116,9 +137,17 @@ app {
}
@media (max-width: 767.98px) {
.main .top-row {
.main .top-row:not(.auth) {
display: none;
}
.main .top-row.auth {
justify-content: space-between;
}
.main .top-row a, .main .top-row .btn-link {
margin-left: 0;
}
}
@media (min-width: 768px) {

View File

@ -0,0 +1,34 @@
// 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.
namespace System.ComponentModel.DataAnnotations
{
/// <summary>
/// A <see cref="ValidationAttribute"/> that compares two properties
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class ComparePropertyAttribute : CompareAttribute
{
/// <summary>
/// Initializes a new instance of <see cref="BlazorCompareAttribute"/>.
/// </summary>
/// <param name="otherProperty">The property to compare with the current property.</param>
public ComparePropertyAttribute(string otherProperty)
: base(otherProperty)
{
}
/// <inheritdoc />
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var validationResult = base.IsValid(value, validationContext);
if (validationResult == ValidationResult.Success)
{
return validationResult;
}
return new ValidationResult(validationResult.ErrorMessage, new[] { validationContext.MemberName });
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Description>Provides experimental support for validation using DataAnnotations.</Description>
<IsShippingPackage>true</IsShippingPackage>
<HasReferenceAssembly>false</HasReferenceAssembly>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Components.Forms" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,125 @@
// 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.ComponentModel.DataAnnotations;
using System.Linq;
namespace Microsoft.AspNetCore.Components.Forms
{
public class ObjectGraphDataAnnotationsValidator : ComponentBase
{
private static readonly object ValidationContextValidatorKey = new object();
private static readonly object ValidatedObjectsKey = new object();
private ValidationMessageStore _validationMessageStore;
[CascadingParameter]
internal EditContext EditContext { get; set; }
protected override void OnInitialized()
{
_validationMessageStore = new ValidationMessageStore(EditContext);
// Perform object-level validation (starting from the root model) on request
EditContext.OnValidationRequested += (sender, eventArgs) =>
{
_validationMessageStore.Clear();
ValidateObject(EditContext.Model, new HashSet<object>());
EditContext.NotifyValidationStateChanged();
};
// Perform per-field validation on each field edit
EditContext.OnFieldChanged += (sender, eventArgs) =>
ValidateField(EditContext, _validationMessageStore, eventArgs.FieldIdentifier);
}
internal void ValidateObject(object value, HashSet<object> visited)
{
if (value is null)
{
return;
}
if (!visited.Add(value))
{
// Already visited this object.
return;
}
if (value is IEnumerable<object> enumerable)
{
var index = 0;
foreach (var item in enumerable)
{
ValidateObject(item, visited);
index++;
}
return;
}
var validationResults = new List<ValidationResult>();
ValidateObject(value, visited, validationResults);
// Transfer results to the ValidationMessageStore
foreach (var validationResult in validationResults)
{
if (!validationResult.MemberNames.Any())
{
_validationMessageStore.Add(new FieldIdentifier(value, string.Empty), validationResult.ErrorMessage);
continue;
}
foreach (var memberName in validationResult.MemberNames)
{
var fieldIdentifier = new FieldIdentifier(value, memberName);
_validationMessageStore.Add(fieldIdentifier, validationResult.ErrorMessage);
}
}
}
private void ValidateObject(object value, HashSet<object> visited, List<ValidationResult> validationResults)
{
var validationContext = new ValidationContext(value);
validationContext.Items.Add(ValidationContextValidatorKey, this);
validationContext.Items.Add(ValidatedObjectsKey, visited);
Validator.TryValidateObject(value, validationContext, validationResults, validateAllProperties: true);
}
internal static bool TryValidateRecursive(object value, ValidationContext validationContext)
{
if (validationContext.Items.TryGetValue(ValidationContextValidatorKey, out var result) && result is ObjectGraphDataAnnotationsValidator validator)
{
var visited = (HashSet<object>)validationContext.Items[ValidatedObjectsKey];
validator.ValidateObject(value, visited);
return true;
}
return false;
}
private static void ValidateField(EditContext editContext, ValidationMessageStore messages, in FieldIdentifier fieldIdentifier)
{
// DataAnnotations only validates public properties, so that's all we'll look for
var propertyInfo = fieldIdentifier.Model.GetType().GetProperty(fieldIdentifier.FieldName);
if (propertyInfo != null)
{
var propertyValue = propertyInfo.GetValue(fieldIdentifier.Model);
var validationContext = new ValidationContext(fieldIdentifier.Model)
{
MemberName = propertyInfo.Name
};
var results = new List<ValidationResult>();
Validator.TryValidateProperty(propertyValue, validationContext, results);
messages.Clear(fieldIdentifier);
messages.Add(fieldIdentifier, results.Select(result => result.ErrorMessage));
// We have to notify even if there were no messages before and are still no messages now,
// because the "state" that changed might be the completion of some async validation task
editContext.NotifyValidationStateChanged();
}
}
}
}

View File

@ -0,0 +1,30 @@
// 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.Components.Forms;
namespace System.ComponentModel.DataAnnotations
{
/// <summary>
/// A <see cref="ValidationAttribute"/> that indicates that the property is a complex or collection type that further needs to be validated.
/// <para>
/// By default <see cref="Validator"/> does not recurse in to complex property types during validation.
/// When used in conjunction with <see cref="ObjectGraphDataAnnotationsValidator"/>, this property allows the validation system to validate
/// complex or collection type properties.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class ValidateComplexTypeAttribute : ValidationAttribute
{
/// <inheritdoc />
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (!ObjectGraphDataAnnotationsValidator.TryValidateRecursive(value, validationContext))
{
throw new InvalidOperationException($"{nameof(ValidateComplexTypeAttribute)} can only used with {nameof(ObjectGraphDataAnnotationsValidator)}.");
}
return ValidationResult.Success;
}
}
}

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,540 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNetCore.Components.Forms;
using Xunit;
namespace Microsoft.AspNetCore.Components
{
public class ObjectGraphDataAnnotationsValidatorTest
{
public class SimpleModel
{
[Required]
public string Name { get; set; }
[Range(1, 16)]
public int Age { get; set; }
}
[Fact]
public void ValidateObject_SimpleObject()
{
var model = new SimpleModel
{
Age = 23,
};
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(() => model.Name);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => model.Age);
Assert.Single(messages);
Assert.Equal(2, editContext.GetValidationMessages().Count());
}
[Fact]
public void ValidateObject_SimpleObject_AllValid()
{
var model = new SimpleModel { Name = "Test", Age = 5 };
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(() => model.Name);
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => model.Age);
Assert.Empty(messages);
Assert.Empty(editContext.GetValidationMessages());
}
public class ModelWithComplexProperty
{
[Required]
public string Property1 { get; set; }
[ValidateComplexType]
public SimpleModel SimpleModel { get; set; }
}
[Fact]
public void ValidateObject_NullComplexProperty()
{
var model = new ModelWithComplexProperty();
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(() => model.Property1);
Assert.Single(messages);
Assert.Single(editContext.GetValidationMessages());
}
[Fact]
public void ValidateObject_ModelWithComplexProperties()
{
var model = new ModelWithComplexProperty { SimpleModel = new SimpleModel() };
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(() => model.Property1);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => model.SimpleModel);
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => model.SimpleModel.Age);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => model.SimpleModel.Name);
Assert.Single(messages);
Assert.Equal(3, editContext.GetValidationMessages().Count());
}
[Fact]
public void ValidateObject_ModelWithComplexProperties_SomeValid()
{
var model = new ModelWithComplexProperty
{
Property1 = "Value",
SimpleModel = new SimpleModel { Name = "Some Value" },
};
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(() => model.Property1);
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => model.SimpleModel);
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => model.SimpleModel.Age);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => model.SimpleModel.Name);
Assert.Empty(messages);
Assert.Single(editContext.GetValidationMessages());
}
public class TestValidatableObject : IValidatableObject
{
[Required]
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
yield return new ValidationResult("Custom validation error");
}
}
public class ModelWithValidatableComplexProperty
{
[Required]
public string Property1 { get; set; }
[ValidateComplexType]
public TestValidatableObject Property2 { get; set; } = new TestValidatableObject();
}
[Fact]
public void ValidateObject_ValidatableComplexProperty()
{
var model = new ModelWithValidatableComplexProperty();
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(() => model.Property1);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => model.Property2);
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => model.Property2.Name);
Assert.Single(messages);
Assert.Equal(2, editContext.GetValidationMessages().Count());
}
[Fact]
public void ValidateObject_ValidatableComplexProperty_ValidatesIValidatableProperty()
{
var model = new ModelWithValidatableComplexProperty
{
Property2 = new TestValidatableObject { Name = "test" },
};
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(() => model.Property1);
Assert.Single(messages);
messages = editContext.GetValidationMessages(new FieldIdentifier(model.Property2, string.Empty));
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => model.Property2.Name);
Assert.Empty(messages);
Assert.Equal(2, editContext.GetValidationMessages().Count());
}
[Fact]
public void ValidateObject_ModelIsIValidatable_PropertyHasError()
{
var model = new TestValidatableObject();
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(new FieldIdentifier(model, string.Empty));
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => model.Name);
Assert.Single(messages);
Assert.Single(editContext.GetValidationMessages());
}
[Fact]
public void ValidateObject_ModelIsIValidatable_ModelHasError()
{
var model = new TestValidatableObject { Name = "test" };
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(new FieldIdentifier(model, string.Empty));
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => model.Name);
Assert.Empty(messages);
Assert.Single(editContext.GetValidationMessages());
}
[Fact]
public void ValidateObject_CollectionModel()
{
var model = new List<SimpleModel>
{
new SimpleModel(),
new SimpleModel { Name = "test", },
};
var editContext = Validate(model);
var item = model[0];
var messages = editContext.GetValidationMessages(new FieldIdentifier(model, "0"));
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => item.Name);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => item.Age);
Assert.Single(messages);
item = model[1];
messages = editContext.GetValidationMessages(new FieldIdentifier(model, "1"));
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => item.Name);
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => item.Age);
Assert.Single(messages);
Assert.Equal(3, editContext.GetValidationMessages().Count());
}
[Fact]
public void ValidateObject_CollectionValidatableModel()
{
var model = new List<TestValidatableObject>
{
new TestValidatableObject(),
new TestValidatableObject { Name = "test", },
};
var editContext = Validate(model);
var item = model[0];
var messages = editContext.GetValidationMessages(() => item.Name);
Assert.Single(messages);
item = model[1];
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => item.Name);
Assert.Empty(messages);
Assert.Equal(2, editContext.GetValidationMessages().Count());
}
private class Level1Validation
{
[ValidateComplexType]
public Level2Validation Level2 { get; set; }
}
public class Level2Validation
{
[ValidateComplexType]
public SimpleModel Level3 { get; set; }
}
[Fact]
public void ValidateObject_ManyLevels()
{
var model = new Level1Validation
{
Level2 = new Level2Validation
{
Level3 = new SimpleModel
{
Age = 47,
}
}
};
var editContext = Validate(model);
var level3 = model.Level2.Level3;
var messages = editContext.GetValidationMessages(() => level3.Name);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => level3.Age);
Assert.Single(messages);
Assert.Equal(2, editContext.GetValidationMessages().Count());
}
private class Person
{
[Required]
public string Name { get; set; }
[ValidateComplexType]
public Person Related { get; set; }
}
[Fact]
public void ValidateObject_RecursiveRelation()
{
var model = new Person { Related = new Person() };
model.Related.Related = model;
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(() => model.Name);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => model.Related.Name);
Assert.Single(messages);
Assert.Equal(2, editContext.GetValidationMessages().Count());
}
[Fact]
public void ValidateObject_RecursiveRelation_OverManySteps()
{
var person1 = new Person();
var person2 = new Person { Name = "Valid name" };
var person3 = new Person();
var person4 = new Person();
person1.Related = person2;
person2.Related = person3;
person3.Related = person4;
person4.Related = person1;
var editContext = Validate(person1);
var messages = editContext.GetValidationMessages(() => person1.Name);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => person2.Name);
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => person3.Name);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => person4.Name);
Assert.Single(messages);
Assert.Equal(3, editContext.GetValidationMessages().Count());
}
private class Node
{
[Required]
public string Id { get; set; }
[ValidateComplexType]
public List<Node> Related { get; set; } = new List<Node>();
}
[Fact]
public void ValidateObject_RecursiveRelation_ViaCollection()
{
var node1 = new Node();
var node2 = new Node { Id = "Valid Id" };
var node3 = new Node();
node1.Related.Add(node2);
node2.Related.Add(node3);
node3.Related.Add(node1);
var editContext = Validate(node1);
var messages = editContext.GetValidationMessages(() => node1.Id);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => node2.Id);
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => node3.Id);
Assert.Single(messages);
Assert.Equal(2, editContext.GetValidationMessages().Count());
}
[Fact]
public void ValidateObject_RecursiveRelation_InCollection()
{
var person1 = new Person();
var person2 = new Person { Name = "Valid name" };
var person3 = new Person();
var person4 = new Person();
person1.Related = person2;
person2.Related = person3;
person3.Related = person4;
person4.Related = person1;
var editContext = Validate(person1);
var messages = editContext.GetValidationMessages(() => person1.Name);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => person2.Name);
Assert.Empty(messages);
messages = editContext.GetValidationMessages(() => person3.Name);
Assert.Single(messages);
messages = editContext.GetValidationMessages(() => person4.Name);
Assert.Single(messages);
Assert.Equal(3, editContext.GetValidationMessages().Count());
}
[Fact]
public void ValidateField_PropertyValid()
{
var model = new SimpleModel { Age = 1 };
var fieldIdentifier = FieldIdentifier.Create(() => model.Age);
var editContext = ValidateField(model, fieldIdentifier);
var messages = editContext.GetValidationMessages(fieldIdentifier);
Assert.Empty(messages);
Assert.Empty(editContext.GetValidationMessages());
}
[Fact]
public void ValidateField_PropertyInvalid()
{
var model = new SimpleModel { Age = 42 };
var fieldIdentifier = FieldIdentifier.Create(() => model.Age);
var editContext = ValidateField(model, fieldIdentifier);
var messages = editContext.GetValidationMessages(fieldIdentifier);
Assert.Single(messages);
Assert.Single(editContext.GetValidationMessages());
}
[Fact]
public void ValidateField_AfterSubmitValidation()
{
var model = new SimpleModel { Age = 42 };
var fieldIdentifier = FieldIdentifier.Create(() => model.Age);
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(fieldIdentifier);
Assert.Single(messages);
Assert.Equal(2, editContext.GetValidationMessages().Count());
model.Age = 4;
editContext.NotifyFieldChanged(fieldIdentifier);
messages = editContext.GetValidationMessages(fieldIdentifier);
Assert.Empty(messages);
Assert.Single(editContext.GetValidationMessages());
}
[Fact]
public void ValidateField_ModelWithComplexProperty()
{
var model = new ModelWithComplexProperty
{
SimpleModel = new SimpleModel { Age = 1 },
};
var fieldIdentifier = FieldIdentifier.Create(() => model.SimpleModel.Name);
var editContext = ValidateField(model, fieldIdentifier);
var messages = editContext.GetValidationMessages(fieldIdentifier);
Assert.Single(messages);
Assert.Single(editContext.GetValidationMessages());
}
[Fact]
public void ValidateField_ModelWithComplexProperty_AfterSubmitValidation()
{
var model = new ModelWithComplexProperty
{
Property1 = "test",
SimpleModel = new SimpleModel { Age = 29, Name = "Test" },
};
var fieldIdentifier = FieldIdentifier.Create(() => model.SimpleModel.Age);
var editContext = Validate(model);
var messages = editContext.GetValidationMessages(fieldIdentifier);
Assert.Single(messages);
model.SimpleModel.Age = 9;
editContext.NotifyFieldChanged(fieldIdentifier);
messages = editContext.GetValidationMessages(fieldIdentifier);
Assert.Empty(messages);
Assert.Empty(editContext.GetValidationMessages());
}
private static EditContext Validate(object model)
{
var editContext = new EditContext(model);
var validator = new TestObjectGraphDataAnnotationsValidator { EditContext = editContext, };
validator.OnInitialized();
editContext.Validate();
return editContext;
}
private static EditContext ValidateField(object model, in FieldIdentifier field)
{
var editContext = new EditContext(model);
var validator = new TestObjectGraphDataAnnotationsValidator { EditContext = editContext, };
validator.OnInitialized();
editContext.NotifyFieldChanged(field);
return editContext;
}
private class TestObjectGraphDataAnnotationsValidator : ObjectGraphDataAnnotationsValidator
{
public new void OnInitialized() => base.OnInitialized();
}
}
}

View File

@ -1,9 +1,19 @@
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a, .btn-link {
color: #0366d6;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
app {
position: relative;
display: flex;
@ -21,10 +31,21 @@ app {
}
.main .top-row {
background-color: #e6e6e6;
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
}
.main .top-row > a, .main .top-row .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
}
.main .top-row a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
@ -44,20 +65,20 @@ app {
top: -2px;
}
.nav-item {
.sidebar .nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
}
.nav-item:first-of-type {
.sidebar .nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
.sidebar .nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item a {
.sidebar .nav-item a {
color: #d7d7d7;
border-radius: 4px;
height: 3rem;
@ -66,12 +87,12 @@ app {
line-height: 3rem;
}
.nav-item a.active {
.sidebar .nav-item a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
.nav-item a:hover {
.sidebar .nav-item a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
@ -96,9 +117,36 @@ app {
color: red;
}
@media (max-width: 767.98px) {
.main .top-row {
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
@media (max-width: 767.98px) {
.main .top-row:not(.auth) {
display: none;
}
.main .top-row.auth {
justify-content: space-between;
}
.main .top-row a, .main .top-row .btn-link {
margin-left: 0;
}
}

View File

@ -240,6 +240,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ignitor", "Ignitor\src\Igni
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ignitor.Test", "Ignitor\test\Ignitor.Test.csproj", "{F31E8118-014E-4CCE-8A48-5282F7B9BB3E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Validation", "Validation", "{FD9BD646-9D50-42ED-A3E1-90558BA0C6B2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.DataAnnotations.Validation", "Blazor\Validation\src\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj", "{B70F90C7-2696-4050-B24E-BF0308F4E059}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests", "Blazor\Validation\test\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests.csproj", "{A5617A9D-C71E-44DE-936C-27611EB40A02}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1486,6 +1492,30 @@ Global
{F31E8118-014E-4CCE-8A48-5282F7B9BB3E}.Release|x64.Build.0 = Release|Any CPU
{F31E8118-014E-4CCE-8A48-5282F7B9BB3E}.Release|x86.ActiveCfg = Release|Any CPU
{F31E8118-014E-4CCE-8A48-5282F7B9BB3E}.Release|x86.Build.0 = Release|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Debug|x64.ActiveCfg = Debug|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Debug|x64.Build.0 = Debug|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Debug|x86.ActiveCfg = Debug|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Debug|x86.Build.0 = Debug|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Release|Any CPU.Build.0 = Release|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Release|x64.ActiveCfg = Release|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Release|x64.Build.0 = Release|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Release|x86.ActiveCfg = Release|Any CPU
{B70F90C7-2696-4050-B24E-BF0308F4E059}.Release|x86.Build.0 = Release|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Debug|x64.ActiveCfg = Debug|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Debug|x64.Build.0 = Debug|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Debug|x86.ActiveCfg = Debug|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Debug|x86.Build.0 = Debug|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|Any CPU.Build.0 = Release|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x64.ActiveCfg = Release|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x64.Build.0 = Release|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x86.ActiveCfg = Release|Any CPU
{A5617A9D-C71E-44DE-936C-27611EB40A02}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1596,6 +1626,9 @@ Global
{BBF37AF9-8290-4B70-8BA8-0F6017B3B620} = {46E4300C-5726-4108-B9A2-18BB94EB26ED}
{CD0EF85C-4187-4515-A355-E5A0D4485F40} = {BDE2397D-C53A-4783-8B3A-1F54F48A6926}
{F31E8118-014E-4CCE-8A48-5282F7B9BB3E} = {BDE2397D-C53A-4783-8B3A-1F54F48A6926}
{FD9BD646-9D50-42ED-A3E1-90558BA0C6B2} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
{B70F90C7-2696-4050-B24E-BF0308F4E059} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
{A5617A9D-C71E-44DE-936C-27611EB40A02} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CC3C47E1-AD1A-4619-9CD3-E08A0148E5CE}

View File

@ -207,7 +207,10 @@ namespace Microsoft.AspNetCore.Components
public sealed partial class EventHandlerAttribute : System.Attribute
{
public EventHandlerAttribute(string attributeName, System.Type eventArgsType) { }
public EventHandlerAttribute(string attributeName, System.Type eventArgsType, bool enableStopPropagation, bool enablePreventDefault) { }
public string AttributeName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public bool EnablePreventDefault { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public bool EnableStopPropagation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public System.Type EventArgsType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
public partial interface IComponent

View File

@ -207,7 +207,10 @@ namespace Microsoft.AspNetCore.Components
public sealed partial class EventHandlerAttribute : System.Attribute
{
public EventHandlerAttribute(string attributeName, System.Type eventArgsType) { }
public EventHandlerAttribute(string attributeName, System.Type eventArgsType, bool enableStopPropagation, bool enablePreventDefault) { }
public string AttributeName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public bool EnablePreventDefault { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public bool EnableStopPropagation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public System.Type EventArgsType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
public partial interface IComponent

View File

@ -16,7 +16,18 @@ namespace Microsoft.AspNetCore.Components
/// </summary>
/// <param name="attributeName"></param>
/// <param name="eventArgsType"></param>
public EventHandlerAttribute(string attributeName, Type eventArgsType)
public EventHandlerAttribute(string attributeName, Type eventArgsType) : this(attributeName, eventArgsType, false, false)
{
}
/// <summary>
/// Constructs an instance of <see cref="EventHandlerAttribute"/>.
/// </summary>
/// <param name="attributeName"></param>
/// <param name="eventArgsType"></param>
/// <param name="enableStopPropagation"></param>
/// <param name="enablePreventDefault"></param>
public EventHandlerAttribute(string attributeName, Type eventArgsType, bool enableStopPropagation, bool enablePreventDefault)
{
if (attributeName == null)
{
@ -30,6 +41,8 @@ namespace Microsoft.AspNetCore.Components
AttributeName = attributeName;
EventArgsType = eventArgsType;
EnableStopPropagation = enableStopPropagation;
EnablePreventDefault = enablePreventDefault;
}
/// <summary>
@ -41,5 +54,15 @@ namespace Microsoft.AspNetCore.Components
/// Gets the event argument type.
/// </summary>
public Type EventArgsType { get; }
/// <summary>
/// Gets the event's ability to stop propagation.
/// </summary>
public bool EnableStopPropagation { get; }
/// <summary>
/// Gets the event's ability to prevent default event flow.
/// </summary>
public bool EnablePreventDefault { get; }
}
}

View File

@ -15,6 +15,8 @@
"Blazor\\Http\\test\\Microsoft.AspNetCore.Blazor.HttpClient.Tests.csproj",
"Blazor\\Server\\src\\Microsoft.AspNetCore.Blazor.Server.csproj",
"Blazor\\Templates\\src\\Microsoft.AspNetCore.Blazor.Templates.csproj",
"Blazor\\Validation\\src\\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj",
"Blazor\\Validation\\test\\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.Tests.csproj",
"Blazor\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj",
"Blazor\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj",
"Blazor\\testassets\\Microsoft.AspNetCore.Blazor.E2EPerformance\\Microsoft.AspNetCore.Blazor.E2EPerformance.csproj",

View File

@ -52,6 +52,12 @@ namespace Microsoft.AspNetCore.Components.Forms
messages.Clear();
foreach (var validationResult in validationResults)
{
if (!validationResult.MemberNames.Any())
{
messages.Add(new FieldIdentifier(editContext.Model, fieldName: string.Empty), validationResult.ErrorMessage);
continue;
}
foreach (var memberName in validationResult.MemberNames)
{
messages.Add(editContext.Field(memberName), validationResult.ErrorMessage);

View File

@ -13,17 +13,21 @@ using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
#nullable enable
namespace Ignitor
{
public class BlazorClient : IAsyncDisposable
{
private const string MarkerPattern = ".*?<!--Blazor:(.*?)-->.*?";
private HubConnection? _hubConnection;
public BlazorClient()
{
CancellationTokenSource = new CancellationTokenSource();
TaskCompletionSource = new TaskCompletionSource<object>();
CancellationToken = CancellationTokenSource.Token;
TaskCompletionSource = new TaskCompletionSource<object?>();
CancellationTokenSource.Token.Register(() =>
{
@ -31,7 +35,7 @@ namespace Ignitor
});
}
public TimeSpan? DefaultConnectionTimeout { get; set; } = TimeSpan.FromSeconds(10);
public TimeSpan? DefaultConnectionTimeout { get; set; } = TimeSpan.FromSeconds(20);
public TimeSpan? DefaultOperationTimeout { get; set; } = TimeSpan.FromMilliseconds(500);
/// <summary>
@ -44,104 +48,103 @@ namespace Ignitor
/// Gets the collections of operation results that are captured when <see cref="CaptureOperations"/>
/// is true.
/// </summary>
public Operations Operations { get; private set; }
public Operations Operations { get; } = new Operations();
public Func<string, Exception> FormatError { get; set; }
public Func<string, Exception>? FormatError { get; set; }
private CancellationTokenSource CancellationTokenSource { get; }
private CancellationToken CancellationToken => CancellationTokenSource.Token;
private CancellationToken CancellationToken { get; }
private TaskCompletionSource<object> TaskCompletionSource { get; }
private TaskCompletionSource<object?> TaskCompletionSource { get; }
private CancellableOperation<CapturedAttachComponentCall> NextAttachComponentReceived { get; set; }
private CancellableOperation<CapturedAttachComponentCall>? NextAttachComponentReceived { get; set; }
private CancellableOperation<CapturedRenderBatch> NextBatchReceived { get; set; }
private CancellableOperation<CapturedRenderBatch?>? NextBatchReceived { get; set; }
private CancellableOperation<string> NextErrorReceived { get; set; }
private CancellableOperation<string?>? NextErrorReceived { get; set; }
private CancellableOperation<Exception> NextDisconnect { get; set; }
private CancellableOperation<Exception?>? NextDisconnect { get; set; }
private CancellableOperation<CapturedJSInteropCall> NextJSInteropReceived { get; set; }
private CancellableOperation<CapturedJSInteropCall?>? NextJSInteropReceived { get; set; }
private CancellableOperation<string> NextDotNetInteropCompletionReceived { get; set; }
private CancellableOperation<string?>? NextDotNetInteropCompletionReceived { get; set; }
public ILoggerProvider LoggerProvider { get; set; }
public ILoggerProvider LoggerProvider { get; set; } = NullLoggerProvider.Instance;
public bool ConfirmRenderBatch { get; set; } = true;
public event Action<CapturedJSInteropCall> JSInterop;
public event Action<CapturedJSInteropCall>? JSInterop;
public event Action<CapturedRenderBatch> RenderBatchReceived;
public event Action<CapturedRenderBatch>? RenderBatchReceived;
public event Action<string> DotNetInteropCompletion;
public event Action<string>? DotNetInteropCompletion;
public event Action<string> OnCircuitError;
public event Action<string>? OnCircuitError;
public string CircuitId { get; set; }
public string? CircuitId { get; private set; }
public ElementHive Hive { get; set; } = new ElementHive();
public ElementHive Hive { get; } = new ElementHive();
public bool ImplicitWait => DefaultOperationTimeout != null;
public HubConnection HubConnection { get; private set; }
public HubConnection HubConnection => _hubConnection ?? throw new InvalidOperationException("HubConnection has not been initialized.");
public Task<CapturedRenderBatch> PrepareForNextBatch(TimeSpan? timeout)
public Task<CapturedRenderBatch?> PrepareForNextBatch(TimeSpan? timeout)
{
if (NextBatchReceived != null && !NextBatchReceived.Disposed)
{
throw new InvalidOperationException("Invalid state previous task not completed");
}
NextBatchReceived = new CancellableOperation<CapturedRenderBatch>(timeout);
NextBatchReceived = new CancellableOperation<CapturedRenderBatch?>(timeout, CancellationToken);
return NextBatchReceived.Completion.Task;
}
public Task<CapturedJSInteropCall> PrepareForNextJSInterop(TimeSpan? timeout)
public Task<CapturedJSInteropCall?> PrepareForNextJSInterop(TimeSpan? timeout)
{
if (NextJSInteropReceived != null && !NextJSInteropReceived.Disposed)
{
throw new InvalidOperationException("Invalid state previous task not completed");
}
NextJSInteropReceived = new CancellableOperation<CapturedJSInteropCall>(timeout);
NextJSInteropReceived = new CancellableOperation<CapturedJSInteropCall?>(timeout, CancellationToken);
return NextJSInteropReceived.Completion.Task;
}
public Task<string> PrepareForNextDotNetInterop(TimeSpan? timeout)
public Task<string?> PrepareForNextDotNetInterop(TimeSpan? timeout)
{
if (NextDotNetInteropCompletionReceived != null && !NextDotNetInteropCompletionReceived.Disposed)
{
throw new InvalidOperationException("Invalid state previous task not completed");
}
NextDotNetInteropCompletionReceived = new CancellableOperation<string>(timeout);
NextDotNetInteropCompletionReceived = new CancellableOperation<string?>(timeout, CancellationToken);
return NextDotNetInteropCompletionReceived.Completion.Task;
}
public Task<string> PrepareForNextCircuitError(TimeSpan? timeout)
public Task<string?> PrepareForNextCircuitError(TimeSpan? timeout)
{
if (NextErrorReceived != null && !NextErrorReceived.Disposed)
{
throw new InvalidOperationException("Invalid state previous task not completed");
}
NextErrorReceived = new CancellableOperation<string>(timeout);
NextErrorReceived = new CancellableOperation<string?>(timeout, CancellationToken);
return NextErrorReceived.Completion.Task;
}
public Task<Exception> PrepareForNextDisconnect(TimeSpan? timeout)
public Task<Exception?> PrepareForNextDisconnect(TimeSpan? timeout)
{
if (NextDisconnect != null && !NextDisconnect.Disposed)
{
throw new InvalidOperationException("Invalid state previous task not completed");
}
NextDisconnect = new CancellableOperation<Exception>(timeout);
NextDisconnect = new CancellableOperation<Exception?>(timeout, CancellationToken);
return NextDisconnect.Completion.Task;
}
@ -172,44 +175,44 @@ namespace Ignitor
return ExpectRenderBatch(() => elementNode.SelectAsync(HubConnection, value));
}
public async Task<CapturedRenderBatch> ExpectRenderBatch(Func<Task> action, TimeSpan? timeout = null)
public async Task<CapturedRenderBatch?> ExpectRenderBatch(Func<Task> action, TimeSpan? timeout = null)
{
var task = WaitForRenderBatch(timeout);
await action();
return await task;
}
public async Task<CapturedJSInteropCall> ExpectJSInterop(Func<Task> action, TimeSpan? timeout = null)
public async Task<CapturedJSInteropCall?> ExpectJSInterop(Func<Task> action, TimeSpan? timeout = null)
{
var task = WaitForJSInterop(timeout);
await action();
return await task;
}
public async Task<string> ExpectDotNetInterop(Func<Task> action, TimeSpan? timeout = null)
public async Task<string?> ExpectDotNetInterop(Func<Task> action, TimeSpan? timeout = null)
{
var task = WaitForDotNetInterop(timeout);
await action();
return await task;
}
public async Task<string> ExpectCircuitError(Func<Task> action, TimeSpan? timeout = null)
public async Task<string?> ExpectCircuitError(Func<Task> action, TimeSpan? timeout = null)
{
var task = WaitForCircuitError(timeout);
await action();
return await task;
}
public async Task<Exception> ExpectDisconnect(Func<Task> action, TimeSpan? timeout = null)
public async Task<Exception?> ExpectDisconnect(Func<Task> action, TimeSpan? timeout = null)
{
var task = WaitForDisconnect(timeout);
await action();
return await task;
}
public async Task<(string error, Exception exception)> ExpectCircuitErrorAndDisconnect(Func<Task> action, TimeSpan? timeout = null)
public async Task<(string? error, Exception? exception)> ExpectCircuitErrorAndDisconnect(Func<Task> action, TimeSpan? timeout = null)
{
string error = null;
string? error = default;
// NOTE: timeout is used for each operation individually.
var exception = await ExpectDisconnect(async () =>
@ -220,7 +223,7 @@ namespace Ignitor
return (error, exception);
}
private async Task<CapturedRenderBatch> WaitForRenderBatch(TimeSpan? timeout = null)
private async Task<CapturedRenderBatch?> WaitForRenderBatch(TimeSpan? timeout = null)
{
if (ImplicitWait)
{
@ -233,7 +236,7 @@ namespace Ignitor
{
return await PrepareForNextBatch(timeout ?? DefaultOperationTimeout);
}
catch (OperationCanceledException)
catch (TimeoutException) when (FormatError != null)
{
throw FormatError("Timed out while waiting for batch.");
}
@ -242,7 +245,7 @@ namespace Ignitor
return null;
}
private async Task<CapturedJSInteropCall> WaitForJSInterop(TimeSpan? timeout = null)
private async Task<CapturedJSInteropCall?> WaitForJSInterop(TimeSpan? timeout = null)
{
if (ImplicitWait)
{
@ -255,7 +258,7 @@ namespace Ignitor
{
return await PrepareForNextJSInterop(timeout ?? DefaultOperationTimeout);
}
catch (OperationCanceledException)
catch (TimeoutException) when (FormatError != null)
{
throw FormatError("Timed out while waiting for JS Interop.");
}
@ -264,7 +267,7 @@ namespace Ignitor
return null;
}
private async Task<string> WaitForDotNetInterop(TimeSpan? timeout = null)
private async Task<string?> WaitForDotNetInterop(TimeSpan? timeout = null)
{
if (ImplicitWait)
{
@ -277,7 +280,7 @@ namespace Ignitor
{
return await PrepareForNextDotNetInterop(timeout ?? DefaultOperationTimeout);
}
catch (OperationCanceledException)
catch (TimeoutException) when (FormatError != null)
{
throw FormatError("Timed out while waiting for .NET interop.");
}
@ -286,7 +289,7 @@ namespace Ignitor
return null;
}
private async Task<string> WaitForCircuitError(TimeSpan? timeout = null)
private async Task<string?> WaitForCircuitError(TimeSpan? timeout = null)
{
if (ImplicitWait)
{
@ -299,7 +302,7 @@ namespace Ignitor
{
return await PrepareForNextCircuitError(timeout ?? DefaultOperationTimeout);
}
catch (OperationCanceledException)
catch (TimeoutException) when (FormatError != null)
{
throw FormatError("Timed out while waiting for circuit error.");
}
@ -308,7 +311,7 @@ namespace Ignitor
return null;
}
private async Task<Exception> WaitForDisconnect(TimeSpan? timeout = null)
private async Task<Exception?> WaitForDisconnect(TimeSpan? timeout = null)
{
if (ImplicitWait)
{
@ -321,7 +324,7 @@ namespace Ignitor
{
return await PrepareForNextDisconnect(timeout ?? DefaultOperationTimeout);
}
catch (OperationCanceledException)
catch (TimeoutException) when (FormatError != null)
{
throw FormatError("Timed out while waiting for disconnect.");
}
@ -344,7 +347,7 @@ namespace Ignitor
}
});
HubConnection = builder.Build();
_hubConnection = builder.Build();
await HubConnection.StartAsync(CancellationToken);
HubConnection.On<int, string>("JS.AttachComponent", OnAttachComponent);
@ -354,11 +357,6 @@ namespace Ignitor
HubConnection.On<string>("JS.Error", OnError);
HubConnection.Closed += OnClosedAsync;
if (CaptureOperations)
{
Operations = new Operations();
}
if (!connectAutomatically)
{
return true;
@ -366,7 +364,7 @@ namespace Ignitor
var descriptors = await GetPrerenderDescriptors(uri);
await ExpectRenderBatch(
async () => CircuitId = await HubConnection.InvokeAsync<string>("StartCircuit", uri, uri, descriptors),
async () => CircuitId = await HubConnection.InvokeAsync<string>("StartCircuit", uri, uri, descriptors, CancellationToken),
DefaultConnectionTimeout);
return CircuitId != null;
}
@ -415,9 +413,9 @@ namespace Ignitor
NextBatchReceived?.Completion?.TrySetResult(null);
}
public Task ConfirmBatch(int batchId, string error = null)
public Task ConfirmBatch(int batchId, string? error = null)
{
return HubConnection.InvokeAsync("OnRenderCompleted", batchId, error);
return HubConnection.InvokeAsync("OnRenderCompleted", batchId, error, CancellationToken);
}
private void OnError(string error)
@ -468,7 +466,14 @@ namespace Ignitor
public async Task InvokeDotNetMethod(object callId, string assemblyName, string methodIdentifier, object dotNetObjectId, string argsJson)
{
await ExpectDotNetInterop(() => HubConnection.InvokeAsync("BeginInvokeDotNetFromJS", callId?.ToString(), assemblyName, methodIdentifier, dotNetObjectId ?? 0, argsJson));
await ExpectDotNetInterop(() => HubConnection.InvokeAsync(
"BeginInvokeDotNetFromJS",
callId?.ToString(),
assemblyName,
methodIdentifier,
dotNetObjectId ?? 0,
argsJson,
CancellationToken));
}
public async Task<string> GetPrerenderDescriptors(Uri uri)
@ -484,10 +489,13 @@ namespace Ignitor
}
public void Cancel()
{
if (!CancellationTokenSource.IsCancellationRequested)
{
CancellationTokenSource.Cancel();
CancellationTokenSource.Dispose();
}
}
public ElementNode FindElementById(string id)
{
@ -519,6 +527,7 @@ namespace Ignitor
public async ValueTask DisposeAsync()
{
Cancel();
if (HubConnection != null)
{
await HubConnection.DisposeAsync();
@ -526,3 +535,5 @@ namespace Ignitor
}
}
}
#nullable restore

View File

@ -5,11 +5,12 @@ using System;
using System.Threading;
using System.Threading.Tasks;
#nullable enable
namespace Ignitor
{
internal class CancellableOperation<TResult>
{
public CancellableOperation(TimeSpan? timeout)
public CancellableOperation(TimeSpan? timeout, CancellationToken cancellationToken)
{
Timeout = timeout;
@ -17,26 +18,38 @@ namespace Ignitor
Completion.Task.ContinueWith(
(task, state) =>
{
var operation = (CancellableOperation<TResult>)state;
var operation = (CancellableOperation<TResult>)state!;
operation.Dispose();
},
this,
TaskContinuationOptions.ExecuteSynchronously); // We need to execute synchronously to clean-up before anything else continues
Cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
if (Timeout != null && Timeout != System.Threading.Timeout.InfiniteTimeSpan && Timeout != TimeSpan.MaxValue)
{
Cancellation = new CancellationTokenSource(Timeout.Value);
Cancellation.CancelAfter(Timeout.Value);
}
CancellationRegistration = Cancellation.Token.Register(
(self) =>
{
var operation = (CancellableOperation<TResult>)self;
operation.Completion.TrySetCanceled(operation.Cancellation.Token);
operation.Cancellation.Dispose();
var operation = (CancellableOperation<TResult>)self!;
if (cancellationToken.IsCancellationRequested)
{
// The operation was externally canceled before it timed out.
Dispose();
return;
}
operation.Completion.TrySetException(new TimeoutException($"The operation timed out after {Timeout}."));
operation.Cancellation?.Dispose();
operation.CancellationRegistration.Dispose();
},
this);
}
}
public TimeSpan? Timeout { get; }
@ -62,3 +75,4 @@ namespace Ignitor
}
}
}
#nullable restore

View File

@ -3,6 +3,7 @@
namespace Ignitor
{
#nullable enable
public class ComponentState
{
public ComponentState(int componentId)
@ -11,6 +12,7 @@ namespace Ignitor
}
public int ComponentId { get; }
public IComponent Component { get; }
public IComponent? Component { get; }
}
#nullable restore
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
#nullable enable
namespace Ignitor
{
public abstract class ContainerNode : Node
@ -82,3 +83,4 @@ namespace Ignitor
}
}
}
#nullable restore

View File

@ -3,7 +3,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
#nullable enable
namespace Ignitor
{
public class ElementHive
@ -35,7 +37,7 @@ namespace Ignitor
}
}
public bool TryFindElementById(string id, out ElementNode element)
public bool TryFindElementById(string id, [NotNullWhen(true)] out ElementNode? element)
{
foreach (var kvp in Components)
{
@ -49,7 +51,7 @@ namespace Ignitor
element = null;
return false;
bool TryGetElementFromChildren(Node node, out ElementNode foundNode)
bool TryGetElementFromChildren(Node node, out ElementNode? foundNode)
{
if (node is ElementNode elementNode &&
elementNode.Attributes.TryGetValue("id", out var elementId) &&
@ -81,6 +83,7 @@ namespace Ignitor
{
component = new ComponentNode(componentId);
Components.Add(componentId, component);
}
ApplyEdits(batch, component, 0, edits);
@ -199,7 +202,7 @@ namespace Ignitor
case RenderTreeEditType.StepOut:
{
parent = parent.Parent;
parent = parent.Parent ?? throw new InvalidOperationException($"Cannot step out of {parent}");
currentDepth--;
childIndexAtCurrentDepth = currentDepth == 0 ? childIndex : 0; // The childIndex is only ever nonzero at zero depth
break;
@ -469,3 +472,4 @@ namespace Ignitor
}
}
}
#nullable restore

View File

@ -7,6 +7,7 @@ using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
#nullable enable
namespace Ignitor
{
public class ElementNode : ContainerNode
@ -87,7 +88,7 @@ namespace Ignitor
return DispatchEventCore(connection, Serialize(webEventDescriptor), Serialize(args));
}
public Task ClickAsync(HubConnection connection)
internal Task ClickAsync(HubConnection connection)
{
if (!Events.TryGetValue("click", out var clickEventDescriptor))
{
@ -129,3 +130,4 @@ namespace Ignitor
}
}
}
#nullable restore

View File

@ -1,10 +1,12 @@
// 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.
#nullable enable
namespace Ignitor
{
public class Error
{
public string Stack { get; set; }
public string? Stack { get; set; }
}
}
#nullable restore

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
// 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.
namespace Ignitor
{

View File

@ -1,10 +1,14 @@
// 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.
#nullable enable
namespace Ignitor
{
public abstract class Node
{
public virtual ContainerNode Parent { get; set; }
public virtual ContainerNode? Parent { get; set; }
}
}
#nullable restore

View File

@ -4,6 +4,8 @@
using System;
using System.IO;
#nullable enable
namespace Ignitor
{
internal static class NodeSerializer
@ -96,7 +98,7 @@ namespace Ignitor
if (attribute.Value != null)
{
Write("=\"");
Write(attribute.Value.ToString());
Write(attribute.Value.ToString()!);
Write("\"");
}
}
@ -113,7 +115,7 @@ namespace Ignitor
if (properties.Value != null)
{
Write("=\"");
Write(properties.Value.ToString());
Write(properties.Value.ToString()!);
Write("\"");
}
}
@ -194,3 +196,4 @@ namespace Ignitor
}
}
}
#nullable restore

View File

@ -3,6 +3,7 @@
using System.Collections.Concurrent;
#nullable enable
namespace Ignitor
{
public sealed class Operations
@ -18,3 +19,4 @@ namespace Ignitor
public ConcurrentQueue<CapturedJSInteropCall> JSInteropCalls { get; } = new ConcurrentQueue<CapturedJSInteropCall>();
}
}
#nullable restore

View File

@ -4,6 +4,8 @@
using System;
using System.Text;
#nullable enable
namespace Ignitor
{
public static class RenderBatchReader
@ -206,7 +208,7 @@ namespace Ignitor
return new ArrayRange<ulong>(Array.Empty<ulong>(), 0);
}
private static string ReadString(ReadOnlySpan<byte> data, string[] strings)
private static string? ReadString(ReadOnlySpan<byte> data, string[] strings)
{
var index = BitConverter.ToInt32(data.Slice(0, 4));
return index >= 0 ? strings[index] : null;
@ -279,3 +281,4 @@ namespace Ignitor
}
}
}
#nullable restore

View File

@ -12,6 +12,7 @@
<Reference Include="Microsoft.AspNetCore.HttpsPolicy" />
<Reference Include="Microsoft.AspNetCore.Mvc" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation" />
</ItemGroup>
</Project>

View File

@ -30,66 +30,72 @@ app {
flex: 1;
}
.main .top-row {
.main .top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
}
}
.main .top-row > a {
.main .top-row > a, .main .top-row .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
}
.main .top-row a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.sidebar .top-row {
.sidebar .top-row {
background-color: rgba(0,0,0,0.4);
}
}
.sidebar .navbar-brand {
.sidebar .navbar-brand {
font-size: 1.1rem;
}
}
.sidebar .oi {
.sidebar .oi {
width: 2rem;
font-size: 1.1rem;
vertical-align: text-top;
top: -2px;
}
}
.nav-item {
.sidebar .nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
}
.nav-item:first-of-type {
.sidebar .nav-item:first-of-type {
padding-top: 1rem;
}
}
.nav-item:last-of-type {
.sidebar .nav-item:last-of-type {
padding-bottom: 1rem;
}
}
.nav-item a {
.sidebar .nav-item a {
color: #d7d7d7;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
}
}
.nav-item a.active {
.sidebar .nav-item a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
}
.nav-item a:hover {
.sidebar .nav-item a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
}
.content {
padding-top: 1.1rem;
@ -131,9 +137,17 @@ app {
}
@media (max-width: 767.98px) {
.main .top-row {
.main .top-row:not(.auth) {
display: none;
}
.main .top-row.auth {
justify-content: space-between;
}
.main .top-row a, .main .top-row .btn-link {
margin-left: 0;
}
}
@media (min-width: 768px) {

View File

@ -12,6 +12,7 @@ using MessagePack;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Protocol;
namespace Microsoft.AspNetCore.Components.Server.BlazorPack
@ -19,6 +20,7 @@ namespace Microsoft.AspNetCore.Components.Server.BlazorPack
/// <summary>
/// Implements the SignalR Hub Protocol using MessagePack with limited type support.
/// </summary>
[NonDefaultHubProtocol]
internal sealed class BlazorPackHubProtocol : IHubProtocol
{
internal const string ProtocolName = "blazorpack";
@ -78,7 +80,7 @@ namespace Microsoft.AspNetCore.Components.Server.BlazorPack
message = PingMessage.Instance;
return true;
case HubProtocolConstants.CloseMessageType:
message = CreateCloseMessage(ref reader);
message = CreateCloseMessage(ref reader, itemCount);
return true;
default:
// Future protocol changes can add message types, old clients can ignore them
@ -196,10 +198,23 @@ namespace Microsoft.AspNetCore.Components.Server.BlazorPack
return ApplyHeaders(headers, new CancelInvocationMessage(invocationId));
}
private static CloseMessage CreateCloseMessage(ref MessagePackReader reader)
private static CloseMessage CreateCloseMessage(ref MessagePackReader reader, int itemCount)
{
var error = ReadString(ref reader, "error");
return new CloseMessage(error);
var allowReconnect = false;
if (itemCount > 2)
{
allowReconnect = ReadBoolean(ref reader, "allowReconnect");
}
// An empty string is still an error
if (error == null && !allowReconnect)
{
return CloseMessage.Empty;
}
return new CloseMessage(error, allowReconnect);
}
private static Dictionary<string, string> ReadHeaders(ref MessagePackReader reader)
@ -515,7 +530,7 @@ namespace Microsoft.AspNetCore.Components.Server.BlazorPack
private void WriteCloseMessage(CloseMessage message, ref MessagePackWriter writer)
{
writer.WriteArrayHeader(2);
writer.WriteArrayHeader(3);
writer.Write(HubProtocolConstants.CloseMessageType);
if (string.IsNullOrEmpty(message.Error))
{
@ -525,6 +540,8 @@ namespace Microsoft.AspNetCore.Components.Server.BlazorPack
{
writer.Write(message.Error);
}
writer.Write(message.AllowReconnect);
}
private void WritePingMessage(PingMessage _, ref MessagePackWriter writer)
@ -559,6 +576,17 @@ namespace Microsoft.AspNetCore.Components.Server.BlazorPack
return destination;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool ReadBoolean(ref MessagePackReader reader, string field)
{
if (reader.End || reader.NextMessagePackType != MessagePackType.Boolean)
{
ThrowInvalidDataException(field, "Boolean");
}
return reader.ReadBoolean();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int ReadInt32(ref MessagePackReader reader, string field)
{

View File

@ -0,0 +1,13 @@
// 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;
namespace Microsoft.AspNetCore.SignalR.Internal
{
// Tells SignalR not to add the IHubProtocol with this attribute to all hubs by default
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
internal class NonDefaultHubProtocolAttribute : Attribute
{
}
}

View File

@ -545,7 +545,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
else
{
return $"There was an unhandled exception on the current circuit, so this circuit will be terminated. For more details turn on " +
$"detailed exceptions in '{typeof(CircuitOptions).Name}.{nameof(CircuitOptions.DetailedErrors)}'. {additionalInformation}";
$"detailed exceptions by setting 'DetailedErrors: true' in 'appSettings.Development.json' or set '{typeof(CircuitOptions).Name}.{nameof(CircuitOptions.DetailedErrors)}'. {additionalInformation}";
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -26,7 +26,7 @@ export function attachDebuggerHotkey(loadAssemblyUrls: string[]) {
if (!hasReferencedPdbs) {
console.error('Cannot start debugging, because the application was not compiled with debugging enabled.');
} else if (!currentBrowserIsChrome) {
console.error('Currently, only Chrome is supported for debugging.');
console.error('Currently, only Edge(Chromium) or Chrome is supported for debugging.');
} else {
launchDebugger();
}

View File

@ -125,6 +125,8 @@ namespace Microsoft.AspNetCore.Components.Forms
public ValidationSummary() { }
[Microsoft.AspNetCore.Components.ParameterAttribute(CaptureUnmatchedValues=true)]
public System.Collections.Generic.IReadOnlyDictionary<string, object> AdditionalAttributes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public object Model { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected virtual void Dispose(bool disposing) { }
protected override void OnParametersSet() { }
@ -222,97 +224,97 @@ namespace Microsoft.AspNetCore.Components.Web
public string Message { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onabort", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onactivate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforeactivate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforecopy", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforecut", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforedeactivate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforepaste", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onblur", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncanplay", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncanplaythrough", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onchange", typeof(Microsoft.AspNetCore.Components.ChangeEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onclick", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncontextmenu", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncopy", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncuechange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncut", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondblclick", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondeactivate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondrag", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragend", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragenter", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragleave", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragover", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragstart", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondrop", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondurationchange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onemptied", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onended", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onerror", typeof(Microsoft.AspNetCore.Components.Web.ErrorEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocus", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocusin", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocusout", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfullscreenchange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfullscreenerror", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ongotpointercapture", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oninput", typeof(Microsoft.AspNetCore.Components.ChangeEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oninvalid", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeydown", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeypress", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeyup", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onload", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadeddata", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadedmetadata", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadend", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadstart", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onlostpointercapture", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousedown", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousemove", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseout", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseover", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseup", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousewheel", typeof(Microsoft.AspNetCore.Components.Web.WheelEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpaste", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpause", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onplay", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onplaying", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointercancel", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerdown", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerenter", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerleave", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerlockchange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerlockerror", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointermove", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerout", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerover", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerup", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onprogress", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onratechange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onreadystatechange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onreset", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onscroll", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onseeked", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onseeking", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselect", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselectionchange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselectstart", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onstalled", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onstop", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onsubmit", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onsuspend", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontimeout", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontimeupdate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchcancel", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchend", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchenter", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchleave", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchmove", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchstart", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onvolumechange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onwaiting", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onwheel", typeof(Microsoft.AspNetCore.Components.Web.WheelEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onabort", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onactivate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforeactivate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforecopy", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforecut", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforedeactivate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforepaste", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onblur", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncanplay", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncanplaythrough", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onchange", typeof(Microsoft.AspNetCore.Components.ChangeEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onclick", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncontextmenu", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncopy", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncuechange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncut", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondblclick", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondeactivate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondrag", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragend", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragenter", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragleave", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragover", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragstart", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondrop", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondurationchange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onemptied", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onended", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onerror", typeof(Microsoft.AspNetCore.Components.Web.ErrorEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocus", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocusin", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocusout", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfullscreenchange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfullscreenerror", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ongotpointercapture", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oninput", typeof(Microsoft.AspNetCore.Components.ChangeEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oninvalid", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeydown", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeypress", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeyup", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onload", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadeddata", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadedmetadata", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadend", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadstart", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onlostpointercapture", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousedown", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousemove", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseout", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseover", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseup", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousewheel", typeof(Microsoft.AspNetCore.Components.Web.WheelEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpaste", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpause", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onplay", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onplaying", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointercancel", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerdown", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerenter", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerleave", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerlockchange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerlockerror", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointermove", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerout", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerover", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerup", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onprogress", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onratechange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onreadystatechange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onreset", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onscroll", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onseeked", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onseeking", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselect", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselectionchange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselectstart", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onstalled", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onstop", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onsubmit", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onsuspend", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontimeout", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontimeupdate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchcancel", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchend", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchenter", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchleave", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchmove", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchstart", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onvolumechange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onwaiting", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onwheel", typeof(Microsoft.AspNetCore.Components.Web.WheelEventArgs), true, true)]
public static partial class EventHandlers
{
}

View File

@ -125,6 +125,8 @@ namespace Microsoft.AspNetCore.Components.Forms
public ValidationSummary() { }
[Microsoft.AspNetCore.Components.ParameterAttribute(CaptureUnmatchedValues=true)]
public System.Collections.Generic.IReadOnlyDictionary<string, object> AdditionalAttributes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public object Model { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected virtual void Dispose(bool disposing) { }
protected override void OnParametersSet() { }
@ -222,97 +224,97 @@ namespace Microsoft.AspNetCore.Components.Web
public string Message { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onabort", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onactivate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforeactivate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforecopy", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforecut", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforedeactivate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforepaste", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onblur", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncanplay", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncanplaythrough", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onchange", typeof(Microsoft.AspNetCore.Components.ChangeEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onclick", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncontextmenu", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncopy", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncuechange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncut", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondblclick", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondeactivate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondrag", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragend", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragenter", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragleave", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragover", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragstart", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondrop", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondurationchange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onemptied", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onended", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onerror", typeof(Microsoft.AspNetCore.Components.Web.ErrorEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocus", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocusin", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocusout", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfullscreenchange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfullscreenerror", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ongotpointercapture", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oninput", typeof(Microsoft.AspNetCore.Components.ChangeEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oninvalid", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeydown", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeypress", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeyup", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onload", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadeddata", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadedmetadata", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadend", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadstart", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onlostpointercapture", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousedown", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousemove", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseout", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseover", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseup", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousewheel", typeof(Microsoft.AspNetCore.Components.Web.WheelEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpaste", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpause", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onplay", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onplaying", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointercancel", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerdown", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerenter", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerleave", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerlockchange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerlockerror", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointermove", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerout", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerover", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerup", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onprogress", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onratechange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onreadystatechange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onreset", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onscroll", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onseeked", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onseeking", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselect", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselectionchange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselectstart", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onstalled", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onstop", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onsubmit", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onsuspend", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontimeout", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontimeupdate", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchcancel", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchend", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchenter", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchleave", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchmove", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchstart", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onvolumechange", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onwaiting", typeof(System.EventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onwheel", typeof(Microsoft.AspNetCore.Components.Web.WheelEventArgs))]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onabort", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onactivate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforeactivate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforecopy", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforecut", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforedeactivate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onbeforepaste", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onblur", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncanplay", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncanplaythrough", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onchange", typeof(Microsoft.AspNetCore.Components.ChangeEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onclick", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncontextmenu", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncopy", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncuechange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oncut", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondblclick", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondeactivate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondrag", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragend", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragenter", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragleave", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragover", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondragstart", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondrop", typeof(Microsoft.AspNetCore.Components.Web.DragEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ondurationchange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onemptied", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onended", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onerror", typeof(Microsoft.AspNetCore.Components.Web.ErrorEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocus", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocusin", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfocusout", typeof(Microsoft.AspNetCore.Components.Web.FocusEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfullscreenchange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onfullscreenerror", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ongotpointercapture", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oninput", typeof(Microsoft.AspNetCore.Components.ChangeEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("oninvalid", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeydown", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeypress", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onkeyup", typeof(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onload", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadeddata", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadedmetadata", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadend", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onloadstart", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onlostpointercapture", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousedown", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousemove", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseout", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseover", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmouseup", typeof(Microsoft.AspNetCore.Components.Web.MouseEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onmousewheel", typeof(Microsoft.AspNetCore.Components.Web.WheelEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpaste", typeof(Microsoft.AspNetCore.Components.Web.ClipboardEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpause", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onplay", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onplaying", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointercancel", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerdown", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerenter", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerleave", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerlockchange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerlockerror", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointermove", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerout", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerover", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onpointerup", typeof(Microsoft.AspNetCore.Components.Web.PointerEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onprogress", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onratechange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onreadystatechange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onreset", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onscroll", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onseeked", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onseeking", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselect", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselectionchange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onselectstart", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onstalled", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onstop", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onsubmit", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onsuspend", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontimeout", typeof(Microsoft.AspNetCore.Components.Web.ProgressEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontimeupdate", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchcancel", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchend", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchenter", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchleave", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchmove", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("ontouchstart", typeof(Microsoft.AspNetCore.Components.Web.TouchEventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onvolumechange", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onwaiting", typeof(System.EventArgs), true, true)]
[Microsoft.AspNetCore.Components.EventHandlerAttribute("onwheel", typeof(Microsoft.AspNetCore.Components.Web.WheelEventArgs), true, true)]
public static partial class EventHandlers
{
}

View File

@ -19,6 +19,12 @@ namespace Microsoft.AspNetCore.Components.Forms
private EditContext _previousEditContext;
private readonly EventHandler<ValidationStateChangedEventArgs> _validationStateChangedHandler;
/// <summary>
/// Gets or sets the model to produce the list of validation messages for.
/// When specified, this lists all errors that are associated with the model instance.
/// </summary>
[Parameter] public object Model { get; set; }
/// <summary>
/// Gets or sets a collection of additional attributes that will be applied to the created <c>ul</c> element.
/// </summary>
@ -57,22 +63,31 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// As an optimization, only evaluate the messages enumerable once, and
// only produce the enclosing <ul> if there's at least one message
var messagesEnumerator = CurrentEditContext.GetValidationMessages().GetEnumerator();
if (messagesEnumerator.MoveNext())
var validationMessages = Model is null ?
CurrentEditContext.GetValidationMessages() :
CurrentEditContext.GetValidationMessages(new FieldIdentifier(Model, string.Empty));
var first = true;
foreach (var error in validationMessages)
{
if (first)
{
first = false;
builder.OpenElement(0, "ul");
builder.AddMultipleAttributes(1, AdditionalAttributes);
builder.AddAttribute(2, "class", "validation-errors");
}
do
{
builder.OpenElement(3, "li");
builder.AddAttribute(4, "class", "validation-message");
builder.AddContent(5, messagesEnumerator.Current);
builder.AddContent(5, error);
builder.CloseElement();
}
while (messagesEnumerator.MoveNext());
if (!first)
{
// We have at least one validation message.
builder.CloseElement();
}
}

View File

@ -11,117 +11,117 @@ namespace Microsoft.AspNetCore.Components.Web
/// </summary>
// Focus events
[EventHandler("onfocus", typeof(FocusEventArgs))]
[EventHandler("onblur", typeof(FocusEventArgs))]
[EventHandler("onfocusin", typeof(FocusEventArgs))]
[EventHandler("onfocusout", typeof(FocusEventArgs))]
[EventHandler("onfocus", typeof(FocusEventArgs), true, true)]
[EventHandler("onblur", typeof(FocusEventArgs), true, true)]
[EventHandler("onfocusin", typeof(FocusEventArgs), true, true)]
[EventHandler("onfocusout", typeof(FocusEventArgs), true, true)]
// Mouse events
[EventHandler("onmouseover", typeof(MouseEventArgs))]
[EventHandler("onmouseout", typeof(MouseEventArgs))]
[EventHandler("onmousemove", typeof(MouseEventArgs))]
[EventHandler("onmousedown", typeof(MouseEventArgs))]
[EventHandler("onmouseup", typeof(MouseEventArgs))]
[EventHandler("onclick", typeof(MouseEventArgs))]
[EventHandler("ondblclick", typeof(MouseEventArgs))]
[EventHandler("onwheel", typeof(WheelEventArgs))]
[EventHandler("onmousewheel", typeof(WheelEventArgs))]
[EventHandler("oncontextmenu", typeof(MouseEventArgs))]
[EventHandler("onmouseover", typeof(MouseEventArgs), true, true)]
[EventHandler("onmouseout", typeof(MouseEventArgs), true, true)]
[EventHandler("onmousemove", typeof(MouseEventArgs), true, true)]
[EventHandler("onmousedown", typeof(MouseEventArgs), true, true)]
[EventHandler("onmouseup", typeof(MouseEventArgs), true, true)]
[EventHandler("onclick", typeof(MouseEventArgs), true, true)]
[EventHandler("ondblclick", typeof(MouseEventArgs), true, true)]
[EventHandler("onwheel", typeof(WheelEventArgs), true, true)]
[EventHandler("onmousewheel", typeof(WheelEventArgs), true, true)]
[EventHandler("oncontextmenu", typeof(MouseEventArgs), true, true)]
// Drag events
[EventHandler("ondrag", typeof(DragEventArgs))]
[EventHandler("ondragend", typeof(DragEventArgs))]
[EventHandler("ondragenter", typeof(DragEventArgs))]
[EventHandler("ondragleave", typeof(DragEventArgs))]
[EventHandler("ondragover", typeof(DragEventArgs))]
[EventHandler("ondragstart", typeof(DragEventArgs))]
[EventHandler("ondrop", typeof(DragEventArgs))]
[EventHandler("ondrag", typeof(DragEventArgs), true, true)]
[EventHandler("ondragend", typeof(DragEventArgs), true, true)]
[EventHandler("ondragenter", typeof(DragEventArgs), true, true)]
[EventHandler("ondragleave", typeof(DragEventArgs), true, true)]
[EventHandler("ondragover", typeof(DragEventArgs), true, true)]
[EventHandler("ondragstart", typeof(DragEventArgs), true, true)]
[EventHandler("ondrop", typeof(DragEventArgs), true, true)]
// Keyboard events
[EventHandler("onkeydown", typeof(KeyboardEventArgs))]
[EventHandler("onkeyup", typeof(KeyboardEventArgs))]
[EventHandler("onkeypress", typeof(KeyboardEventArgs))]
[EventHandler("onkeydown", typeof(KeyboardEventArgs), true, true)]
[EventHandler("onkeyup", typeof(KeyboardEventArgs), true, true)]
[EventHandler("onkeypress", typeof(KeyboardEventArgs), true, true)]
// Input events
[EventHandler("onchange", typeof(ChangeEventArgs))]
[EventHandler("oninput", typeof(ChangeEventArgs))]
[EventHandler("oninvalid", typeof(EventArgs))]
[EventHandler("onreset", typeof(EventArgs))]
[EventHandler("onselect", typeof(EventArgs))]
[EventHandler("onselectstart", typeof(EventArgs))]
[EventHandler("onselectionchange", typeof(EventArgs))]
[EventHandler("onsubmit", typeof(EventArgs))]
[EventHandler("onchange", typeof(ChangeEventArgs), true, true)]
[EventHandler("oninput", typeof(ChangeEventArgs), true, true)]
[EventHandler("oninvalid", typeof(EventArgs), true, true)]
[EventHandler("onreset", typeof(EventArgs), true, true)]
[EventHandler("onselect", typeof(EventArgs), true, true)]
[EventHandler("onselectstart", typeof(EventArgs), true, true)]
[EventHandler("onselectionchange", typeof(EventArgs), true, true)]
[EventHandler("onsubmit", typeof(EventArgs), true, true)]
// Clipboard events
[EventHandler("onbeforecopy", typeof(EventArgs))]
[EventHandler("onbeforecut", typeof(EventArgs))]
[EventHandler("onbeforepaste", typeof(EventArgs))]
[EventHandler("oncopy", typeof(ClipboardEventArgs))]
[EventHandler("oncut", typeof(ClipboardEventArgs))]
[EventHandler("onpaste", typeof(ClipboardEventArgs))]
[EventHandler("onbeforecopy", typeof(EventArgs), true, true)]
[EventHandler("onbeforecut", typeof(EventArgs), true, true)]
[EventHandler("onbeforepaste", typeof(EventArgs), true, true)]
[EventHandler("oncopy", typeof(ClipboardEventArgs), true, true)]
[EventHandler("oncut", typeof(ClipboardEventArgs), true, true)]
[EventHandler("onpaste", typeof(ClipboardEventArgs), true, true)]
// Touch events
[EventHandler("ontouchcancel", typeof(TouchEventArgs))]
[EventHandler("ontouchend", typeof(TouchEventArgs))]
[EventHandler("ontouchmove", typeof(TouchEventArgs))]
[EventHandler("ontouchstart", typeof(TouchEventArgs))]
[EventHandler("ontouchenter", typeof(TouchEventArgs))]
[EventHandler("ontouchleave", typeof(TouchEventArgs))]
[EventHandler("ontouchcancel", typeof(TouchEventArgs), true, true)]
[EventHandler("ontouchend", typeof(TouchEventArgs), true, true)]
[EventHandler("ontouchmove", typeof(TouchEventArgs), true, true)]
[EventHandler("ontouchstart", typeof(TouchEventArgs), true, true)]
[EventHandler("ontouchenter", typeof(TouchEventArgs), true, true)]
[EventHandler("ontouchleave", typeof(TouchEventArgs), true, true)]
// Pointer events
[EventHandler("ongotpointercapture", typeof(PointerEventArgs))]
[EventHandler("onlostpointercapture", typeof(PointerEventArgs))]
[EventHandler("onpointercancel", typeof(PointerEventArgs))]
[EventHandler("onpointerdown", typeof(PointerEventArgs))]
[EventHandler("onpointerenter", typeof(PointerEventArgs))]
[EventHandler("onpointerleave", typeof(PointerEventArgs))]
[EventHandler("onpointermove", typeof(PointerEventArgs))]
[EventHandler("onpointerout", typeof(PointerEventArgs))]
[EventHandler("onpointerover", typeof(PointerEventArgs))]
[EventHandler("onpointerup", typeof(PointerEventArgs))]
[EventHandler("ongotpointercapture", typeof(PointerEventArgs), true, true)]
[EventHandler("onlostpointercapture", typeof(PointerEventArgs), true, true)]
[EventHandler("onpointercancel", typeof(PointerEventArgs), true, true)]
[EventHandler("onpointerdown", typeof(PointerEventArgs), true, true)]
[EventHandler("onpointerenter", typeof(PointerEventArgs), true, true)]
[EventHandler("onpointerleave", typeof(PointerEventArgs), true, true)]
[EventHandler("onpointermove", typeof(PointerEventArgs), true, true)]
[EventHandler("onpointerout", typeof(PointerEventArgs), true, true)]
[EventHandler("onpointerover", typeof(PointerEventArgs), true, true)]
[EventHandler("onpointerup", typeof(PointerEventArgs), true, true)]
// Media events
[EventHandler("oncanplay", typeof(EventArgs))]
[EventHandler("oncanplaythrough", typeof(EventArgs))]
[EventHandler("oncuechange", typeof(EventArgs))]
[EventHandler("ondurationchange", typeof(EventArgs))]
[EventHandler("onemptied", typeof(EventArgs))]
[EventHandler("onpause", typeof(EventArgs))]
[EventHandler("onplay", typeof(EventArgs))]
[EventHandler("onplaying", typeof(EventArgs))]
[EventHandler("onratechange", typeof(EventArgs))]
[EventHandler("onseeked", typeof(EventArgs))]
[EventHandler("onseeking", typeof(EventArgs))]
[EventHandler("onstalled", typeof(EventArgs))]
[EventHandler("onstop", typeof(EventArgs))]
[EventHandler("onsuspend", typeof(EventArgs))]
[EventHandler("ontimeupdate", typeof(EventArgs))]
[EventHandler("onvolumechange", typeof(EventArgs))]
[EventHandler("onwaiting", typeof(EventArgs))]
[EventHandler("oncanplay", typeof(EventArgs), true, true)]
[EventHandler("oncanplaythrough", typeof(EventArgs), true, true)]
[EventHandler("oncuechange", typeof(EventArgs), true, true)]
[EventHandler("ondurationchange", typeof(EventArgs), true, true)]
[EventHandler("onemptied", typeof(EventArgs), true, true)]
[EventHandler("onpause", typeof(EventArgs), true, true)]
[EventHandler("onplay", typeof(EventArgs), true, true)]
[EventHandler("onplaying", typeof(EventArgs), true, true)]
[EventHandler("onratechange", typeof(EventArgs), true, true)]
[EventHandler("onseeked", typeof(EventArgs), true, true)]
[EventHandler("onseeking", typeof(EventArgs), true, true)]
[EventHandler("onstalled", typeof(EventArgs), true, true)]
[EventHandler("onstop", typeof(EventArgs), true, true)]
[EventHandler("onsuspend", typeof(EventArgs), true, true)]
[EventHandler("ontimeupdate", typeof(EventArgs), true, true)]
[EventHandler("onvolumechange", typeof(EventArgs), true, true)]
[EventHandler("onwaiting", typeof(EventArgs), true, true)]
// Progress events
[EventHandler("onloadstart", typeof(ProgressEventArgs))]
[EventHandler("ontimeout", typeof(ProgressEventArgs))]
[EventHandler("onabort", typeof(ProgressEventArgs))]
[EventHandler("onload", typeof(ProgressEventArgs))]
[EventHandler("onloadend", typeof(ProgressEventArgs))]
[EventHandler("onprogress", typeof(ProgressEventArgs))]
[EventHandler("onerror", typeof(ErrorEventArgs))]
[EventHandler("onloadstart", typeof(ProgressEventArgs), true, true)]
[EventHandler("ontimeout", typeof(ProgressEventArgs), true, true)]
[EventHandler("onabort", typeof(ProgressEventArgs), true, true)]
[EventHandler("onload", typeof(ProgressEventArgs), true, true)]
[EventHandler("onloadend", typeof(ProgressEventArgs), true, true)]
[EventHandler("onprogress", typeof(ProgressEventArgs), true, true)]
[EventHandler("onerror", typeof(ErrorEventArgs), true, true)]
// General events
[EventHandler("onactivate", typeof(EventArgs))]
[EventHandler("onbeforeactivate", typeof(EventArgs))]
[EventHandler("onbeforedeactivate", typeof(EventArgs))]
[EventHandler("ondeactivate", typeof(EventArgs))]
[EventHandler("onended", typeof(EventArgs))]
[EventHandler("onfullscreenchange", typeof(EventArgs))]
[EventHandler("onfullscreenerror", typeof(EventArgs))]
[EventHandler("onloadeddata", typeof(EventArgs))]
[EventHandler("onloadedmetadata", typeof(EventArgs))]
[EventHandler("onpointerlockchange", typeof(EventArgs))]
[EventHandler("onpointerlockerror", typeof(EventArgs))]
[EventHandler("onreadystatechange", typeof(EventArgs))]
[EventHandler("onscroll", typeof(EventArgs))]
[EventHandler("onactivate", typeof(EventArgs), true, true)]
[EventHandler("onbeforeactivate", typeof(EventArgs), true, true)]
[EventHandler("onbeforedeactivate", typeof(EventArgs), true, true)]
[EventHandler("ondeactivate", typeof(EventArgs), true, true)]
[EventHandler("onended", typeof(EventArgs), true, true)]
[EventHandler("onfullscreenchange", typeof(EventArgs), true, true)]
[EventHandler("onfullscreenerror", typeof(EventArgs), true, true)]
[EventHandler("onloadeddata", typeof(EventArgs), true, true)]
[EventHandler("onloadedmetadata", typeof(EventArgs), true, true)]
[EventHandler("onpointerlockchange", typeof(EventArgs), true, true)]
[EventHandler("onpointerlockerror", typeof(EventArgs), true, true)]
[EventHandler("onreadystatechange", typeof(EventArgs), true, true)]
[EventHandler("onscroll", typeof(EventArgs), true, true)]
public static class EventHandlers
{
}

View File

@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
{
// Arrange
var expectedError = $"There was an unhandled exception on the current circuit, so this circuit will be terminated. For more details turn on " +
$"detailed exceptions in 'CircuitOptions.DetailedErrors'. Bad input data.";
$"detailed exceptions by setting 'DetailedErrors: true' in 'appSettings.Development.json' or set 'CircuitOptions.DetailedErrors'. Bad input data.";
var eventDescriptor = Serialize(new WebEventDescriptor()
{
@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
{
// Arrange
var expectedError = $"There was an unhandled exception on the current circuit, so this circuit will be terminated. For more details turn on " +
$"detailed exceptions in 'CircuitOptions.DetailedErrors'. Failed to dispatch event.";
$"detailed exceptions by setting 'DetailedErrors: true' in 'appSettings.Development.json' or set 'CircuitOptions.DetailedErrors'. Failed to dispatch event.";
var eventDescriptor = Serialize(new WebEventDescriptor()
{
@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
{
// Arrange
var expectedError = $"There was an unhandled exception on the current circuit, so this circuit will be terminated. For more details turn on " +
$"detailed exceptions in 'CircuitOptions.DetailedErrors'. Failed to complete render batch '1846'.";
$"detailed exceptions by setting 'DetailedErrors: true' in 'appSettings.Development.json' or set 'CircuitOptions.DetailedErrors'. Failed to complete render batch '1846'.";
Client.ConfirmRenderBatch = false;

View File

@ -216,7 +216,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
{
// Arrange
var expectedError = "There was an unhandled exception on the current circuit, so this circuit will be terminated. " +
"For more details turn on detailed exceptions in 'CircuitOptions.DetailedErrors'. " +
"For more details turn on detailed exceptions by setting 'DetailedErrors: true' in 'appSettings.Development.json' or set 'CircuitOptions.DetailedErrors'. " +
"Location change to 'http://example.com' failed.";
var rootUri = ServerFixture.RootUri;
@ -245,7 +245,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
{
// Arrange
var expectedError = "There was an unhandled exception on the current circuit, so this circuit will be terminated. " +
"For more details turn on detailed exceptions in 'CircuitOptions.DetailedErrors'. " +
"For more details turn on detailed exceptions by setting 'DetailedErrors: true' in 'appSettings.Development.json' or set 'CircuitOptions.DetailedErrors'. " +
"Location change failed.";
var rootUri = ServerFixture.RootUri;

View File

@ -75,11 +75,6 @@ namespace Microsoft.AspNetCore.Components
async Task IAsyncLifetime.DisposeAsync()
{
if (TestSink != null)
{
TestSink.MessageLogged -= TestSink_MessageLogged;
}
await DisposeAsync();
}
@ -90,6 +85,11 @@ namespace Microsoft.AspNetCore.Components
protected virtual Task DisposeAsync()
{
if (TestSink != null)
{
TestSink.MessageLogged -= TestSink_MessageLogged;
}
return Task.CompletedTask;
}
@ -97,8 +97,19 @@ namespace Microsoft.AspNetCore.Components
{
var log = new LogMessage(context.LogLevel, context.EventId, context.Message, context.Exception);
Logs.Enqueue(log);
try
{
// This might produce an InvalidOperationException when the logger tries to log a message after
// the test has completed but before the handler has been removed.
// ---> System.InvalidOperationException: There is no currently active test.
// For that reason, we capture the exception here and silence it, as the message is captured inside the Logs
// variable anyway.
Output.WriteLine(log.ToString());
}
catch (Exception)
{
}
}
[DebuggerDisplay("{LogLevel.ToString(),nq} - {Message ?? \"null\",nq} - {Exception?.Message,nq}")]
protected sealed class LogMessage

View File

@ -1,18 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using BasicTestApp;
using BasicTestApp.FormsTest;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
using Microsoft.AspNetCore.Testing;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
@ -34,14 +33,20 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
}
protected virtual IWebElement MountSimpleValidationComponent()
=> Browser.MountTestComponent<SimpleValidationComponent>();
protected virtual IWebElement MountTypicalValidationComponent()
=> Browser.MountTestComponent<TypicalValidationComponent>();
[Fact]
public async Task EditFormWorksWithDataAnnotationsValidator()
{
var appElement = Browser.MountTestComponent<SimpleValidationComponent>();
var appElement = MountSimpleValidationComponent();;
var form = appElement.FindElement(By.TagName("form"));
var userNameInput = appElement.FindElement(By.ClassName("user-name")).FindElement(By.TagName("input"));
var acceptsTermsInput = appElement.FindElement(By.ClassName("accepts-terms")).FindElement(By.TagName("input"));
var submitButton = appElement.FindElement(By.TagName("button"));
var submitButton = appElement.FindElement(By.CssSelector("button[type=submit]"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
// The form emits unmatched attributes
@ -77,7 +82,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void InputTextInteractsWithEditContext()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var nameInput = appElement.FindElement(By.ClassName("name")).FindElement(By.TagName("input"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
@ -104,7 +109,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void InputNumberInteractsWithEditContext_NonNullableInt()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var ageInput = appElement.FindElement(By.ClassName("age")).FindElement(By.TagName("input"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
@ -136,7 +141,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void InputNumberInteractsWithEditContext_NullableFloat()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var heightInput = appElement.FindElement(By.ClassName("height")).FindElement(By.TagName("input"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
@ -160,7 +165,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void InputTextAreaInteractsWithEditContext()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var descriptionInput = appElement.FindElement(By.ClassName("description")).FindElement(By.TagName("textarea"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
@ -187,7 +192,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void InputDateInteractsWithEditContext_NonNullableDateTime()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var renewalDateInput = appElement.FindElement(By.ClassName("renewal-date")).FindElement(By.TagName("input"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
@ -218,7 +223,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void InputDateInteractsWithEditContext_NullableDateTimeOffset()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var expiryDateInput = appElement.FindElement(By.ClassName("expiry-date")).FindElement(By.TagName("input"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
@ -241,7 +246,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void InputSelectInteractsWithEditContext()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var ticketClassInput = new SelectElement(appElement.FindElement(By.ClassName("ticket-class")).FindElement(By.TagName("select")));
var select = ticketClassInput.WrappedElement;
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
@ -263,7 +268,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void InputCheckboxInteractsWithEditContext()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var acceptsTermsInput = appElement.FindElement(By.ClassName("accepts-terms")).FindElement(By.TagName("input"));
var isEvilInput = appElement.FindElement(By.ClassName("is-evil")).FindElement(By.TagName("input"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
@ -297,7 +302,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
var appElement = Browser.MountTestComponent<NotifyPropertyChangedValidationComponent>();
var userNameInput = appElement.FindElement(By.ClassName("user-name")).FindElement(By.TagName("input"));
var acceptsTermsInput = appElement.FindElement(By.ClassName("accepts-terms")).FindElement(By.TagName("input"));
var submitButton = appElement.FindElement(By.TagName("button"));
var submitButton = appElement.FindElement(By.CssSelector("button[type=submit]"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);
var submissionStatus = appElement.FindElement(By.Id("submission-status"));
@ -331,11 +336,11 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void ValidationMessageDisplaysMessagesForField()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var emailContainer = appElement.FindElement(By.ClassName("email"));
var emailInput = emailContainer.FindElement(By.TagName("input"));
var emailMessagesAccessor = CreateValidationMessagesAccessor(emailContainer);
var submitButton = appElement.FindElement(By.TagName("button"));
var submitButton = appElement.FindElement(By.CssSelector("button[type=submit]"));
// Doesn't show messages for other fields
submitButton.Click();
@ -355,10 +360,43 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
Browser.Empty(emailMessagesAccessor);
}
[Fact]
public void ErrorsFromCompareAttribute()
{
var appElement = MountTypicalValidationComponent();
var emailContainer = appElement.FindElement(By.ClassName("email"));
var emailInput = emailContainer.FindElement(By.TagName("input"));
var confirmEmailContainer = appElement.FindElement(By.ClassName("confirm-email"));
var confirmInput = confirmEmailContainer.FindElement(By.TagName("input"));
var confirmEmailValidationMessage = CreateValidationMessagesAccessor(confirmEmailContainer);
var modelErrors = CreateValidationMessagesAccessor(appElement.FindElement(By.ClassName("model-errors")));
CreateValidationMessagesAccessor(emailContainer);
var submitButton = appElement.FindElement(By.CssSelector("button[type=submit]"));
// Updates on edit
emailInput.SendKeys("a@b.com\t");
submitButton.Click();
Browser.Empty(confirmEmailValidationMessage);
Browser.Equal(new[] { "Email and confirm email do not match." }, modelErrors);
confirmInput.SendKeys("not-test@example.com\t");
Browser.Equal(new[] { "Email and confirm email do not match." }, confirmEmailValidationMessage);
// Can become correct
confirmInput.Clear();
confirmInput.SendKeys("a@b.com\t");
Browser.Empty(confirmEmailValidationMessage);
submitButton.Click();
Browser.Empty(modelErrors);
}
[Fact]
public void InputComponentsCauseContainerToRerenderOnChange()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
var appElement = MountTypicalValidationComponent();
var ticketClassInput = new SelectElement(appElement.FindElement(By.ClassName("ticket-class")).FindElement(By.TagName("select")));
var selectedTicketClassDisplay = appElement.FindElement(By.Id("selected-ticket-class"));
var messagesAccessor = CreateValidationMessagesAccessor(appElement);

View File

@ -0,0 +1,90 @@
// 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 BasicTestApp.FormsTest;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Components.E2ETest.Tests
{
public class FormsTestWithExperimentalValidator : FormsTest
{
public FormsTestWithExperimentalValidator(
BrowserFixture browserFixture,
ToggleExecutionModeServerFixture<BasicTestApp.Program> serverFixture,
ITestOutputHelper output) : base(browserFixture, serverFixture, output)
{
}
protected override IWebElement MountSimpleValidationComponent()
=> Browser.MountTestComponent<SimpleValidationComponentUsingExperimentalValidator>();
protected override IWebElement MountTypicalValidationComponent()
=> Browser.MountTestComponent<TypicalValidationComponentUsingExperimentalValidator>();
[Fact]
public void EditFormWorksWithNestedValidation()
{
var appElement = Browser.MountTestComponent<ExperimentalValidationComponent>();
var nameInput = appElement.FindElement(By.CssSelector(".name input"));
var emailInput = appElement.FindElement(By.CssSelector(".email input"));
var confirmEmailInput = appElement.FindElement(By.CssSelector(".confirm-email input"));
var streetInput = appElement.FindElement(By.CssSelector(".street input"));
var zipInput = appElement.FindElement(By.CssSelector(".zip input"));
var countryInput = new SelectElement(appElement.FindElement(By.CssSelector(".country select")));
var descriptionInput = appElement.FindElement(By.CssSelector(".description input"));
var weightInput = appElement.FindElement(By.CssSelector(".weight input"));
var submitButton = appElement.FindElement(By.CssSelector("button[type=submit]"));
submitButton.Click();
Browser.Equal(4, () => appElement.FindElements(By.CssSelector(".all-errors .validation-message")).Count);
Browser.Equal("Enter a name", () => appElement.FindElement(By.CssSelector(".name .validation-message")).Text);
Browser.Equal("Enter an email", () => appElement.FindElement(By.CssSelector(".email .validation-message")).Text);
Browser.Equal("A street address is required.", () => appElement.FindElement(By.CssSelector(".street .validation-message")).Text);
Browser.Equal("Description is required.", () => appElement.FindElement(By.CssSelector(".description .validation-message")).Text);
// Verify class-level validation
nameInput.SendKeys("Some person");
emailInput.SendKeys("test@example.com");
countryInput.SelectByValue("Mordor");
descriptionInput.SendKeys("Fragile staff");
streetInput.SendKeys("Mount Doom\t");
submitButton.Click();
// Verify member validation from IValidatableObject on a model property, CustomValidationAttribute on a model attribute, and BlazorCompareAttribute.
Browser.Equal("A ZipCode is required", () => appElement.FindElement(By.CssSelector(".zip .validation-message")).Text);
Browser.Equal("'Confirm email address' and 'EmailAddress' do not match.", () => appElement.FindElement(By.CssSelector(".confirm-email .validation-message")).Text);
Browser.Equal("Fragile items must be placed in secure containers", () => appElement.FindElement(By.CssSelector(".item-error .validation-message")).Text);
Browser.Equal(3, () => appElement.FindElements(By.CssSelector(".all-errors .validation-message")).Count);
zipInput.SendKeys("98052");
confirmEmailInput.SendKeys("test@example.com");
descriptionInput.Clear();
weightInput.SendKeys("0");
descriptionInput.SendKeys("The One Ring\t");
submitButton.Click();
// Verify validation from IValidatableObject on the model.
Browser.Equal("Some items in your list cannot be delivered.", () => appElement.FindElement(By.CssSelector(".model-errors .validation-message")).Text);
Browser.Single(() => appElement.FindElements(By.CssSelector(".all-errors .validation-message")));
// Let's make sure the form submits
descriptionInput.Clear();
descriptionInput.SendKeys("A different ring\t");
submitButton.Click();
Browser.Empty(() => appElement.FindElements(By.CssSelector(".all-errors .validation-message")));
Browser.Equal("OnValidSubmit", () => appElement.FindElement(By.CssSelector(".submission-log")).Text);
}
}
}

View File

@ -17,6 +17,7 @@
<Reference Include="Microsoft.AspNetCore.Blazor" />
<Reference Include="Microsoft.AspNetCore.Blazor.HttpClient" />
<Reference Include="Microsoft.AspNetCore.Components.Authorization" />
<Reference Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation" />
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,185 @@
@using System.ComponentModel.DataAnnotations
@using Microsoft.AspNetCore.Components.Forms
<p>
This component is used to verify the use of the experimental ObjectGraphDataAnnotationsValidator type with IValidatableObject and deep validation, as well
as the ComparePropertyAttribute.
</p>
<EditForm Model="@model" OnValidSubmit="@HandleValidSubmit">
<ObjectGraphDataAnnotationsValidator />
<p class="name">
Name: <InputText @bind-Value="model.Recipient" placeholder="Enter the recipient" />
<ValidationMessage For="@(() => model.Recipient)" />
</p>
<p class="email">
Email: <InputText @bind-Value="model.EmailAddress" />
<ValidationMessage For="@(() => model.EmailAddress)" />
</p>
<p class="confirm-email">
Confirm Email: <InputText @bind-Value="model.ConfirmEmailAddress" />
<ValidationMessage For="@(() => model.ConfirmEmailAddress)" />
</p>
<fieldset>
<legend>Items to deliver</legend>
<p>
<button id="addItem" type="button" @onclick="AddItem">Add Item</button>
</p>
<ul class="items">
@foreach (var item in model.Items)
{
<li>
<div style="display: inline-flex; flex-direction: row">
<div style="flex-grow: 1" class="description">
<InputText @bind-Value="item.Description" placeholder="Description" />
<ValidationMessage For="@(() => item.Description)" />
</div>
<div style="flex-grow: 1" class="weight">
<InputNumber @bind-Value="item.Weight" />
<ValidationMessage For="@(() => item.Weight)" />
</div>
<div style="flex-grow: 1" class="item-error">
<ValidationSummary Model="item" />
</div>
</div>
</li>
}
</ul>
</fieldset>
<fieldset>
<legend>Shipping details</legend>
<p class="street">
Street Address: <InputText @bind-Value="model.Address.Street" />
<ValidationMessage For="@(() => model.Address.Street)" />
</p>
<p class="zip">
Zip Code: <InputText @bind-Value="model.Address.ZipCode" />
<ValidationMessage For="@(() => model.Address.ZipCode)" />
</p>
<p class="country">
Country:
<InputSelect @bind-Value="model.Address.Country">
<option></option>
<option value="@Country.Gondor">@Country.Gondor</option>
<option value="@Country.Mordor">@Country.Mordor</option>
<option value="@Country.Rohan">@Country.Rohan</option>
<option value="@Country.Shire">@Country.Shire</option>
</InputSelect>
<ValidationMessage For="@(() => model.Address.Country)" />
</p>
<p class="address-validation">
<ValidationSummary Model="model.Address" />
</p>
</fieldset>
<div class="model-errors">
<ValidationSummary Model="model"/>
</div>
<button type="submit">Submit</button>
<div class="all-errors">
<ValidationSummary />
</div>
</EditForm>
<ul class="submission-log">
@foreach (var entry in submissionLog)
{
<li>@entry</li>
}
</ul>
@code {
Delivery model = new Delivery();
public class Delivery : IValidatableObject
{
[Required(ErrorMessage = "Enter a name")]
public string Recipient { get; set; }
[Required(ErrorMessage = "Enter an email")]
[EmailAddress(ErrorMessage = "Enter a valid email address")]
public string EmailAddress { get; set; }
[CompareProperty(nameof(EmailAddress))]
[Display(Name = "Confirm email address")]
public string ConfirmEmailAddress { get; set; }
[ValidateComplexType]
public Address Address { get; } = new Address();
[ValidateComplexType]
public List<Item> Items { get; } = new List<Item>
{
new Item(),
};
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
if (Address.Street == "Mount Doom" && Items.Any(i => i.Description == "The One Ring"))
{
yield return new ValidationResult("Some items in your list cannot be delivered.");
}
}
}
public class Address : IValidatableObject
{
[Required(ErrorMessage = "A street address is required.")]
public string Street { get; set; }
public string ZipCode { get; set; }
[EnumDataType(typeof(Country))]
public Country Country { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
if (Country == Country.Mordor && string.IsNullOrEmpty(ZipCode))
{
yield return new ValidationResult("A ZipCode is required", new[] { nameof(ZipCode) });
}
}
}
[CustomValidation(typeof(Item), nameof(Item.CustomValidate))]
public class Item
{
[Required(ErrorMessage = "Description is required.")]
public string Description { get; set; }
[Range(0.1, 50, ErrorMessage = "Items must weigh between 0.1 and 5")]
public double Weight { get; set; } = 1;
public static ValidationResult CustomValidate(Item item, ValidationContext context)
{
if (item.Weight < 2.0 && item.Description.StartsWith("Fragile"))
{
return new ValidationResult("Fragile items must be placed in secure containers");
}
return ValidationResult.Success;
}
}
public enum Country { Gondor, Mordor, Rohan, Shire }
List<string> submissionLog = new List<string>();
void HandleValidSubmit()
{
submissionLog.Add("OnValidSubmit");
}
void AddItem()
{
model.Items.Add(new Item());
}
}

View File

@ -2,7 +2,14 @@
@using Microsoft.AspNetCore.Components.Forms
<EditForm Model="@this" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit" autocomplete="off">
@if (UseExperimentalValidator)
{
<ObjectGraphDataAnnotationsValidator />
}
else
{
<DataAnnotationsValidator />
}
<p class="user-name">
User name: <input @bind="UserName" class="@context.FieldCssClass(() => UserName)" />
@ -29,6 +36,8 @@
}
@code {
protected virtual bool UseExperimentalValidator => false;
string lastCallback;
[Required(ErrorMessage = "Please choose a username")]

View File

@ -0,0 +1,7 @@
namespace BasicTestApp.FormsTest
{
public class TypicalValidationComponentUsingExperimentalValidator : TypicalValidationComponent
{
protected override bool UseExperimentalValidator => true;
}
}

View File

@ -2,7 +2,14 @@
@using Microsoft.AspNetCore.Components.Forms
<EditForm EditContext="@editContext" OnValidSubmit="@HandleValidSubmit">
@if (UseExperimentalValidator)
{
<ObjectGraphDataAnnotationsValidator />
}
else
{
<DataAnnotationsValidator />
}
<p class="name">
Name: <InputText @bind-Value="person.Name" placeholder="Enter your name" />
@ -11,6 +18,10 @@
Email: <InputText @bind-Value="person.Email" />
<ValidationMessage For="@(() => person.Email)" />
</p>
<p class="confirm-email">
Email: <InputText @bind-Value="person.ConfirmEmail" />
<ValidationMessage For="@(() => person.ConfirmEmail)" />
</p>
<p class="age">
Age (years): <InputNumber @bind-Value="person.AgeInYears" placeholder="Enter your age" />
</p>
@ -49,12 +60,18 @@
<button type="submit">Submit</button>
<p class="model-errors">
<ValidationSummary Model="person" />
</p>
<ValidationSummary />
</EditForm>
<ul>@foreach (var entry in submissionLog) { <li>@entry</li> }</ul>
@code {
protected virtual bool UseExperimentalValidator => false;
Person person = new Person();
EditContext editContext;
ValidationMessageStore customValidationMessageStore;
@ -75,6 +92,9 @@
[StringLength(10, ErrorMessage = "We only accept very short email addresses (max 10 chars)")]
public string Email { get; set; }
[Compare(nameof(Email), ErrorMessage = "Email and confirm email do not match.")]
public string ConfirmEmail { get; set; }
[Range(0, 200, ErrorMessage = "Nobody is that old")]
public int AgeInYears { get; set; }

View File

@ -0,0 +1,7 @@
namespace BasicTestApp.FormsTest
{
public class SimpleValidationComponentUsingExperimentalValidator : SimpleValidationComponent
{
protected override bool UseExperimentalValidator => true;
}
}

View File

@ -29,7 +29,10 @@
<option value="BasicTestApp.FocusEventComponent">Focus events</option>
<option value="BasicTestApp.FormsTest.NotifyPropertyChangedValidationComponent">INotifyPropertyChanged validation</option>
<option value="BasicTestApp.FormsTest.SimpleValidationComponent">Simple validation</option>
<option value="BasicTestApp.FormsTest.SimpleValidationComponentUsingExperimentalValidator">Simple validation using experimental validator</option>
<option value="BasicTestApp.FormsTest.TypicalValidationComponent">Typical validation</option>
<option value="BasicTestApp.FormsTest.TypicalValidationComponentUsingExperimentalValidator">Typical validation using experimental validator</option>
<option value="BasicTestApp.FormsTest.ExperimentalValidationComponent">Experimental validation</option>
<option value="BasicTestApp.GlobalizationBindCases">Globalization Bind Cases</option>
<option value="BasicTestApp.HierarchicalImportsTest.Subdir.ComponentUsingImports">Imports statement</option>
<option value="BasicTestApp.HtmlBlockChildContent">ChildContent HTML Block</option>

View File

@ -4,6 +4,16 @@ html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a, .btn-link {
color: #0366d6;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
app {
position: relative;
display: flex;
@ -21,10 +31,21 @@ app {
}
.main .top-row {
background-color: #e6e6e6;
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
}
.main .top-row > a, .main .top-row .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
}
.main .top-row a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
@ -44,20 +65,20 @@ app {
top: -2px;
}
.nav-item {
.sidebar .nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
}
.nav-item:first-of-type {
.sidebar .nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
.sidebar .nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item a {
.sidebar .nav-item a {
color: #d7d7d7;
border-radius: 4px;
height: 3rem;
@ -66,12 +87,12 @@ app {
line-height: 3rem;
}
.nav-item a.active {
.sidebar .nav-item a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
.nav-item a:hover {
.sidebar .nav-item a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
@ -96,9 +117,36 @@ app {
color: red;
}
@media (max-width: 767.98px) {
.main .top-row {
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
@media (max-width: 767.98px) {
.main .top-row:not(.auth) {
display: none;
}
.main .top-row.auth {
justify-content: space-between;
}
.main .top-row a, .main .top-row .btn-link {
margin-left: 0;
}
}

View File

@ -24,17 +24,15 @@ namespace Microsoft.AspNetCore.Hosting.FunctionalTests
public ShutdownTests(ITestOutputHelper output) : base(output) { }
[ConditionalFact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/2577")]
[ConditionalFact]
[OSSkipCondition(OperatingSystems.Windows)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/2577", FlakyOn.All)]
public async Task ShutdownTestRun()
{
await ExecuteShutdownTest(nameof(ShutdownTestRun), "Run");
}
[ConditionalFact]
[Flaky("https://github.com/aspnet/Hosting/issues/1214", FlakyOn.All)]
[OSSkipCondition(OperatingSystems.Windows)]
[OSSkipCondition(OperatingSystems.MacOSX)]
public async Task ShutdownTestWaitForShutdown()

View File

@ -167,7 +167,7 @@ namespace Microsoft.AspNetCore.WebUtilities
if (!isFinalBlock)
{
// Don't buffer indefinately
if (span.Length > KeyLengthLimit + ValueLengthLimit)
if ((uint)span.Length > (uint)KeyLengthLimit + (uint)ValueLengthLimit)
{
ThrowKeyOrValueTooLargeException();
}
@ -236,7 +236,7 @@ namespace Microsoft.AspNetCore.WebUtilities
if (!isFinalBlock)
{
// Don't buffer indefinately
if ((sequenceReader.Consumed - consumedBytes) > KeyLengthLimit + ValueLengthLimit)
if ((uint)(sequenceReader.Consumed - consumedBytes) > (uint)KeyLengthLimit + (uint)ValueLengthLimit)
{
ThrowKeyOrValueTooLargeException();
}

View File

@ -211,6 +211,28 @@ namespace Microsoft.AspNetCore.WebUtilities
Assert.Equal("", dict["t"]);
}
[Theory]
[MemberData(nameof(Encodings))]
public void TryParseFormValues_LimitsCanBeLarge(Encoding encoding)
{
var readOnlySequence = ReadOnlySequenceFactory.SingleSegmentFactory.CreateWithContent(encoding.GetBytes("foo=bar&baz=boo&t="));
KeyValueAccumulator accumulator = default;
var formReader = new FormPipeReader(null, encoding);
formReader.KeyLengthLimit = int.MaxValue;
formReader.ValueLengthLimit = int.MaxValue;
formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false);
formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true);
Assert.True(readOnlySequence.IsEmpty);
Assert.Equal(3, accumulator.KeyCount);
var dict = accumulator.GetResults();
Assert.Equal("bar", dict["foo"]);
Assert.Equal("boo", dict["baz"]);
Assert.Equal("", dict["t"]);
}
[Theory]
[MemberData(nameof(Encodings))]
public void TryParseFormValues_SplitAcrossSegmentsWorks(Encoding encoding)
@ -230,6 +252,28 @@ namespace Microsoft.AspNetCore.WebUtilities
Assert.Equal("", dict["t"]);
}
[Theory]
[MemberData(nameof(Encodings))]
public void TryParseFormValues_SplitAcrossSegmentsWorks_LimitsCanBeLarge(Encoding encoding)
{
var readOnlySequence = ReadOnlySequenceFactory.SegmentPerByteFactory.CreateWithContent(encoding.GetBytes("foo=bar&baz=boo&t="));
KeyValueAccumulator accumulator = default;
var formReader = new FormPipeReader(null, encoding);
formReader.KeyLengthLimit = int.MaxValue;
formReader.ValueLengthLimit = int.MaxValue;
formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false);
formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true);
Assert.True(readOnlySequence.IsEmpty);
Assert.Equal(3, accumulator.KeyCount);
var dict = accumulator.GetResults();
Assert.Equal("bar", dict["foo"]);
Assert.Equal("boo", dict["baz"]);
Assert.Equal("", dict["t"]);
}
[Theory]
[MemberData(nameof(Encodings))]
public void TryParseFormValues_MultiSegmentWithArrayPoolAcrossSegmentsWorks(Encoding encoding)

View File

@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Identity
{
var key = await manager.GetAuthenticatorKeyAsync(user);
int code;
if (!int.TryParse(token, out code))
if (key == null || !int.TryParse(token, out code))
{
return false;
}

View File

@ -108,7 +108,6 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
public string ReturnUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public virtual System.Threading.Tasks.Task OnGetAsync(string returnUrl = null) { throw null; }
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> OnPostAsync(string returnUrl = null) { throw null; }
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> OnPostSendVerificationEmailAsync() { throw null; }
public partial class InputModel
{
public InputModel() { }
@ -177,7 +176,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
public bool DisplayConfirmAccountLink { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string EmailConfirmationUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> OnGetAsync(string email) { throw null; }
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> OnGetAsync(string email, string returnUrl = null) { throw null; }
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
public abstract partial class RegisterModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
@ -627,7 +626,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
public bool DisplayConfirmAccountLink { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string EmailConfirmationUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> OnGetAsync(string email) { throw null; }
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> OnGetAsync(string email, string returnUrl = null) { throw null; }
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
public abstract partial class RegisterModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel

View File

@ -41,9 +41,6 @@
<p>
<a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
</p>
<p>
<button id="resend-confirmation" type="submit" asp-page-handler="SendVerificationEmail" class="btn-link" style="padding:0px;margin:0px;border:0px">Resend email confirmation</button>
</p>
</div>
</form>
</section>

View File

@ -92,12 +92,6 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Task<IActionResult> OnPostAsync(string returnUrl = null) => throw new NotImplementedException();
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Task<IActionResult> OnPostSendVerificationEmailAsync() => throw new NotImplementedException();
}
internal class LoginModel<TUser> : LoginModel where TUser : class
@ -105,15 +99,12 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
private readonly UserManager<TUser> _userManager;
private readonly SignInManager<TUser> _signInManager;
private readonly ILogger<LoginModel> _logger;
private readonly IEmailSender _emailSender;
public LoginModel(SignInManager<TUser> signInManager, ILogger<LoginModel> logger,
UserManager<TUser> userManager,
IEmailSender emailSender)
UserManager<TUser> userManager)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = logger;
}
@ -169,34 +160,5 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
// If we got this far, something failed, redisplay form
return Page();
}
public override async Task<IActionResult> OnPostSendVerificationEmailAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.FindByEmailAsync(Input.Email);
if (user == null)
{
ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
}
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = userId, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(
Input.Email,
"Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
return Page();
}
}
}

View File

@ -147,7 +147,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code },
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
@ -155,7 +155,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Task<IActionResult> OnGetAsync(string email) => throw new NotImplementedException();
public virtual Task<IActionResult> OnGetAsync(string email, string returnUrl = null) => throw new NotImplementedException();
}
internal class RegisterConfirmationModel<TUser> : RegisterConfirmationModel where TUser : class
@ -57,12 +57,13 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
_sender = sender;
}
public override async Task<IActionResult> OnGetAsync(string email)
public override async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
{
if (email == null)
{
return RedirectToPage("/Index");
}
returnUrl = returnUrl ?? Url.Content("~/");
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
@ -81,7 +82,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
EmailConfirmationUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code },
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
}

View File

@ -146,7 +146,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code },
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
@ -154,7 +154,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual Task<IActionResult> OnGetAsync(string email) => throw new NotImplementedException();
public virtual Task<IActionResult> OnGetAsync(string email, string returnUrl = null) => throw new NotImplementedException();
}
internal class RegisterConfirmationModel<TUser> : RegisterConfirmationModel where TUser : class
@ -57,12 +57,13 @@ namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
_sender = sender;
}
public override async Task<IActionResult> OnGetAsync(string email)
public override async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
{
if (email == null)
{
return RedirectToPage("/Index");
}
returnUrl = returnUrl ?? Url.Content("~/");
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
@ -81,7 +82,7 @@ namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
EmailConfirmationUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code },
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
}

View File

@ -56,12 +56,13 @@
</div>
<footer class="footer border-top pl-3 text-muted">
<div class="container">
&copy; @DateTime.Now.Year - @Environment.ApplicationName -
&copy; @DateTime.Now.Year - @Environment.ApplicationName
@{
var foundPrivacy = Url.Page("/Privacy", new { area = "" });
}
@if (foundPrivacy != null)
{
@:-
<a asp-area="" asp-page="/Privacy">Privacy</a>
}
</div>

View File

@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
});
var registeredLocation = ResponseAssert.IsRedirect(registered);
Assert.Equal(RegisterConfirmation.Path + "?email="+userName, registeredLocation.ToString());
Assert.Equal(RegisterConfirmation.Path + "?email="+userName+"&returnUrl=%2F", registeredLocation.ToString());
var registerResponse = await Client.GetAsync(registeredLocation);
var register = await ResponseAssert.IsHtmlDocumentAsync(registerResponse);

View File

@ -23,7 +23,8 @@
<Target Name="Build" DependsOnTargets="DebBuild" />
<Target Name="Pack" />
<Target Name="DebBuild" DependsOnTargets="$(DebBuildDependsOn)" Condition="'$(IsTargetingPackBuilding)' != 'false'">
<Target Name="DebBuild" DependsOnTargets="$(DebBuildDependsOn)"
Condition="!( '$(IsTargetingPackBuilding)' == 'false' AND '$(MSBuildProjectName)' == 'Debian.TargetingPack' )">
<!-- Generate debian_config.json. We can't simply use WriteLinesToFile because of https://github.com/Microsoft/msbuild/issues/1622. Use our custom GenerateFileFromTemplate task instead -->
<PropertyGroup>
<DebianConfigProperties>

View File

@ -32,7 +32,8 @@
<Target Name="Build" DependsOnTargets="RpmBuild" />
<Target Name="Pack" />
<Target Name="RpmBuild" DependsOnTargets="$(RpmBuildDependsOn)" Condition="'$(IsTargetingPackBuilding)' != 'false'">
<Target Name="RpmBuild" DependsOnTargets="$(RpmBuildDependsOn)"
Condition="!( '$(IsTargetingPackBuilding)' == 'false' AND '$(MSBuildProjectName)' == 'Rpm.TargetingPack' )">
<!-- Create layout: Create changelog -->
<PropertyGroup>
<ChangeLogProps>DATE=$([System.DateTime]::UtcNow.ToString(ddd MMM dd yyyy))</ChangeLogProps>

View File

@ -82,7 +82,8 @@
<BuildDependsOn Condition="'$(IsTargetingPackBuilding)' == 'false'" />
</PropertyGroup>
<Target Name="CreateTargetingPackNugetPackage" AfterTargets="CopyToArtifactsDirectory;Build">
<Target Name="CreateTargetingPackNugetPackage" AfterTargets="CopyToArtifactsDirectory;Build"
Condition="'$(IsTargetingPackBuilding)' != 'false'">
<PropertyGroup>
<MsiFullPath>$(InstallersOutputPath)$(PackageFileName)</MsiFullPath>

View File

@ -882,11 +882,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
public Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataKind MetadataKind { get { throw null; } }
public System.Type ModelType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public System.Reflection.ParameterInfo ParameterInfo { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public System.Reflection.ParameterInfo ParameterInfo { get { throw null; } }
public System.Reflection.PropertyInfo PropertyInfo { get { throw null; } }
public bool Equals(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity other) { throw null; }
public override bool Equals(object obj) { throw null; }
public static Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity ForParameter(System.Reflection.ParameterInfo parameter) { throw null; }
public static Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity ForParameter(System.Reflection.ParameterInfo parameter, System.Type modelType) { throw null; }
public static Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity ForProperty(System.Reflection.PropertyInfo propertyInfo, System.Type modelType, System.Type containerType) { throw null; }
[System.ObsoleteAttribute("This API is obsolete and may be removed in a future release.")]
public static Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity ForProperty(System.Type modelType, string name, System.Type containerType) { throw null; }
public static Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity ForType(System.Type modelType) { throw null; }
public override int GetHashCode() { throw null; }

View File

@ -17,12 +17,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
Type modelType,
string name = null,
Type containerType = null,
ParameterInfo parameterInfo = null)
object fieldInfo = null)
{
ModelType = modelType;
Name = name;
ContainerType = containerType;
ParameterInfo = parameterInfo;
FieldInfo = fieldInfo;
}
/// <summary>
@ -47,6 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
/// <param name="name">The name of the property.</param>
/// <param name="containerType">The container type of the model property.</param>
/// <returns>A <see cref="ModelMetadataIdentity"/>.</returns>
[Obsolete("This API is obsolete and may be removed in a future release.")]
public static ModelMetadataIdentity ForProperty(
Type modelType,
string name,
@ -70,6 +71,36 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
return new ModelMetadataIdentity(modelType, name, containerType);
}
/// <summary>
/// Creates a <see cref="ModelMetadataIdentity"/> for the provided property.
/// </summary>
/// <param name="modelType">The model type.</param>
/// <param name="propertyInfo">The property.</param>
/// <param name="containerType">The container type of the model property.</param>
/// <returns>A <see cref="ModelMetadataIdentity"/>.</returns>
public static ModelMetadataIdentity ForProperty(
PropertyInfo propertyInfo,
Type modelType,
Type containerType)
{
if (propertyInfo == null)
{
throw new ArgumentNullException(nameof(propertyInfo));
}
if (modelType == null)
{
throw new ArgumentNullException(nameof(modelType));
}
if (containerType == null)
{
throw new ArgumentNullException(nameof(containerType));
}
return new ModelMetadataIdentity(modelType, propertyInfo.Name, containerType, fieldInfo: propertyInfo);
}
/// <summary>
/// Creates a <see cref="ModelMetadataIdentity"/> for the provided parameter.
/// </summary>
@ -97,7 +128,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
throw new ArgumentNullException(nameof(modelType));
}
return new ModelMetadataIdentity(modelType, parameter.Name, parameterInfo: parameter);
return new ModelMetadataIdentity(modelType, parameter.Name, fieldInfo: parameter);
}
/// <summary>
@ -139,11 +170,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
/// </summary>
public string Name { get; }
private object FieldInfo { get; }
/// <summary>
/// Gets a descriptor for the parameter, or <c>null</c> if this instance
/// does not represent a parameter.
/// </summary>
public ParameterInfo ParameterInfo { get; }
public ParameterInfo ParameterInfo => FieldInfo as ParameterInfo;
/// <summary>
/// Gets a descriptor for the property, or <c>null</c> if this instance
/// does not represent a property.
/// </summary>
public PropertyInfo PropertyInfo => FieldInfo as PropertyInfo;
/// <inheritdoc />
public bool Equals(ModelMetadataIdentity other)
@ -152,7 +191,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
ContainerType == other.ContainerType &&
ModelType == other.ModelType &&
Name == other.Name &&
ParameterInfo == other.ParameterInfo;
ParameterInfo == other.ParameterInfo &&
PropertyInfo == other.PropertyInfo;
}
/// <inheritdoc />
@ -170,6 +210,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
hash.Add(ModelType);
hash.Add(Name, StringComparer.Ordinal);
hash.Add(ParameterInfo);
hash.Add(PropertyInfo);
return hash;
}
}

View File

@ -270,7 +270,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public void ContainerType_ReturnExpectedMetadata_ForProperty()
{
// Arrange & Act
var metadata = new TestModelMetadata(typeof(int), nameof(string.Length), typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var metadata = new TestModelMetadata(property, typeof(int), typeof(string));
// Assert
Assert.Equal(typeof(string), metadata.ContainerType);
@ -308,7 +309,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public void Names_ReturnExpectedMetadata_ForProperty()
{
// Arrange & Act
var metadata = new TestModelMetadata(typeof(int), nameof(string.Length), typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var metadata = new TestModelMetadata(property, typeof(int), typeof(string));
// Assert
Assert.Equal(nameof(string.Length), metadata.Name);
@ -322,7 +324,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public void GetDisplayName_ReturnsDisplayName_IfSet()
{
// Arrange
var metadata = new TestModelMetadata(typeof(int), "Length", typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var metadata = new TestModelMetadata(property, typeof(int), typeof(string));
metadata.SetDisplayName("displayName");
// Act
@ -351,7 +354,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public void GetDisplayName_ReturnsPropertyName_WhenSetAndDisplayNameIsNull()
{
// Arrange
var metadata = new TestModelMetadata(typeof(int), "Length", typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var metadata = new TestModelMetadata(property, typeof(int), typeof(string));
// Act
var result = metadata.GetDisplayName();
@ -419,8 +423,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
}
public TestModelMetadata(Type modelType, string propertyName, Type containerType)
: base(ModelMetadataIdentity.ForProperty(modelType, propertyName, containerType))
public TestModelMetadata(PropertyInfo propertyInfo, Type modelType, Type containerType)
: base(ModelMetadataIdentity.ForProperty(propertyInfo, modelType, containerType))
{
}

View File

@ -8,7 +8,6 @@ using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters.Json;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc.Formatters
@ -87,6 +86,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
return InputFormatterResult.Failure();
}
catch (Exception exception) when (exception is FormatException || exception is OverflowException)
{
// The code in System.Text.Json never throws these exceptions. However a custom converter could produce these errors for instance when
// parsing a value. These error messages are considered safe to report to users using ModelState.
context.ModelState.TryAddModelError(string.Empty, exception, context.Metadata);
Log.JsonInputException(_logger, exception);
return InputFormatterResult.Failure();
}
finally
{
if (inputStream is TranscodingReadStream transcoding)

View File

@ -190,7 +190,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
private ModelMetadataCacheEntry GetCacheEntry(PropertyInfo property, Type modelType)
{
return _typeCache.GetOrAdd(
ModelMetadataIdentity.ForProperty(modelType, property.Name, property.DeclaringType),
ModelMetadataIdentity.ForProperty(property, modelType, property.DeclaringType),
_cacheEntryFactory);
}
@ -275,8 +275,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var propertyHelper = propertyHelpers[i];
var propertyKey = ModelMetadataIdentity.ForProperty(
propertyHelper.Property,
propertyHelper.Property.PropertyType,
propertyHelper.Name,
key.ModelType);
var propertyEntry = CreateSinglePropertyDetails(propertyKey, propertyHelper);

View File

@ -1,8 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Moq;
using Xunit;
@ -27,7 +25,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var context = new DefaultModelBindingContext();
#pragma warning disable CS0618 // Type or member is obsolete
var identity = ModelMetadataIdentity.ForProperty(typeof(int), property, typeof(string));
#pragma warning restore CS0618 // Type or member is obsolete
context.ModelMetadata = new Mock<ModelMetadata>(identity).Object;
// Act

View File

@ -334,7 +334,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
// Assert
Assert.True(result.HasError, "Model should have produced an error!");
Assert.Collection(formatterContext.ModelState.OrderBy(k => k.Key),
kvp => {
kvp =>
{
Assert.Equal(expectedValue, kvp.Key);
});
}

View File

@ -5,6 +5,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Xunit;
@ -81,6 +83,48 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
});
}
[Fact]
public async Task ReadAsync_DoesNotThrowFormatException()
{
// Arrange
var formatter = GetInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes("{\"dateValue\":\"not-a-date\"}");
var httpContext = GetHttpContext(contentBytes);
var formatterContext = CreateInputFormatterContext(typeof(TypeWithBadConverters), httpContext);
// Act
await formatter.ReadAsync(formatterContext);
Assert.False(formatterContext.ModelState.IsValid);
var kvp = Assert.Single(formatterContext.ModelState);
Assert.Empty(kvp.Key);
var error = Assert.Single(kvp.Value.Errors);
Assert.Equal("The supplied value is invalid.", error.ErrorMessage);
}
[Fact]
public async Task ReadAsync_DoesNotThrowOverflowException()
{
// Arrange
var formatter = GetInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes("{\"shortValue\":\"32768\"}");
var httpContext = GetHttpContext(contentBytes);
var formatterContext = CreateInputFormatterContext(typeof(TypeWithBadConverters), httpContext);
// Act
await formatter.ReadAsync(formatterContext);
Assert.False(formatterContext.ModelState.IsValid);
var kvp = Assert.Single(formatterContext.ModelState);
Assert.Empty(kvp.Key);
var error = Assert.Single(kvp.Value.Errors);
Assert.Equal("The supplied value is invalid.", error.ErrorMessage);
}
protected override TextInputFormatter GetInputFormatter()
{
return new SystemTextJsonInputFormatter(new JsonOptions(), LoggerFactory.CreateLogger<SystemTextJsonInputFormatter>());
@ -99,5 +143,40 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
internal override string ReadAsync_InvalidComplexArray_AddsOverflowErrorsToModelState_Expected => "$[1].Small";
internal override string ReadAsync_ComplexPoco_Expected => "$.Person.Numbers[2]";
private class TypeWithBadConverters
{
[JsonConverter(typeof(DateTimeConverter))]
public DateTime DateValue { get; set; }
[JsonConverter(typeof(ShortConverter))]
public short ShortValue { get; set; }
}
private class ShortConverter : JsonConverter<short>
{
public override short Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return short.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, short value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
private class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
}
}

View File

@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -184,7 +184,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -207,7 +207,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -230,7 +230,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -253,7 +253,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -420,7 +420,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -438,7 +438,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
{
// Arrange
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindRequiredOnClass)),
ModelMetadataIdentity.ForProperty(typeof(BindRequiredOnClass).GetProperty(nameof(BindRequiredOnClass.Property)), typeof(int), typeof(BindRequiredOnClass)),
new ModelAttributes(new object[0], new object[0], null));
var provider = new DefaultBindingMetadataProvider();
@ -456,7 +456,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
{
// Arrange
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindNeverOnClass)),
ModelMetadataIdentity.ForProperty(typeof(BindNeverOnClass).GetProperty(nameof(BindNeverOnClass.Property)), typeof(int), typeof(BindNeverOnClass)),
new ModelAttributes(new object[0], new object[0], null));
var provider = new DefaultBindingMetadataProvider();
@ -474,7 +474,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
{
// Arrange
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(InheritedBindNeverOnClass)),
ModelMetadataIdentity.ForProperty(typeof(BindNeverOnClass).GetProperty(nameof(BindNeverOnClass.Property)), typeof(int), typeof(BindNeverOnClass)),
new ModelAttributes(new object[0], new object[0], null));
var provider = new DefaultBindingMetadataProvider();
@ -497,7 +497,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindNeverOnClass)),
ModelMetadataIdentity.ForProperty(typeof(BindNeverOnClass).GetProperty(nameof(BindNeverOnClass.Property)), typeof(int), typeof(BindNeverOnClass)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -520,7 +520,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindNeverOnClass)),
ModelMetadataIdentity.ForProperty(typeof(BindNeverOnClass).GetProperty(nameof(BindNeverOnClass.Property)), typeof(int), typeof(BindNeverOnClass)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -543,7 +543,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(InheritedBindNeverOnClass)),
ModelMetadataIdentity.ForProperty(typeof(InheritedBindNeverOnClass).GetProperty(nameof(InheritedBindNeverOnClass.Property)), typeof(int), typeof(InheritedBindNeverOnClass)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -566,7 +566,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindRequiredOnClass)),
ModelMetadataIdentity.ForProperty(typeof(BindRequiredOnClass).GetProperty(nameof(BindRequiredOnClass.Property)), typeof(int), typeof(BindRequiredOnClass)),
new ModelAttributes(new object[0], propertyAttributes, null));
var provider = new DefaultBindingMetadataProvider();
@ -585,7 +585,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
{
// Arrange
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindRequiredOverridesInheritedBindNever)),
ModelMetadataIdentity.ForProperty(typeof(BindRequiredOverridesInheritedBindNever).GetProperty(nameof(BindRequiredOverridesInheritedBindNever.Property)), typeof(int), typeof(BindRequiredOverridesInheritedBindNever)),
new ModelAttributes(new object[0], new object[0], null));
var provider = new DefaultBindingMetadataProvider();
@ -641,7 +641,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string)),
new ModelAttributes(typeAttributes, new object[0], null));
// These values shouldn't be changed since this is a Type-Metadata

View File

@ -103,7 +103,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(typeof(string), "Message", typeof(Exception));
var key = ModelMetadataIdentity.ForProperty(typeof(Exception).GetProperty(nameof(Exception.Message)), typeof(string), typeof(Exception));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], new object[0], null));
// Act
@ -123,8 +123,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(
typeof(TypeWithProperties).GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)),
typeof(string),
nameof(TypeWithProperties.PublicGetPublicSetProperty),
typeof(TypeWithProperties));
var attributes = new ModelAttributes(Array.Empty<object>(), Array.Empty<object>(), null);
@ -160,8 +160,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(
typeof(TypeWithProperties).GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)),
typeof(string),
nameof(TypeWithProperties.PublicGetPublicSetProperty),
typeof(TypeWithProperties));
var attributes = new ModelAttributes(Array.Empty<object>(), Array.Empty<object>(), null);
@ -197,8 +197,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(
typeof(TypeWithProperties).GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)),
typeof(string),
nameof(TypeWithProperties.PublicGetPublicSetProperty),
typeof(TypeWithProperties));
var attributes = new ModelAttributes(Array.Empty<object>(), Array.Empty<object>(), null);
@ -393,19 +393,22 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var prop1 = typeof(Exception).GetProperty(nameof(Exception.Message));
var prop2 = typeof(Exception).GetProperty(nameof(Exception.StackTrace));
var expectedProperties = new DefaultModelMetadata[]
{
new DefaultModelMetadata(
provider.Object,
detailsProvider,
new DefaultMetadataDetails(
ModelMetadataIdentity.ForProperty(typeof(int), "Prop1", typeof(string)),
ModelMetadataIdentity.ForProperty(prop1, typeof(int), typeof(string)),
attributes: new ModelAttributes(new object[0], new object[0], null))),
new DefaultModelMetadata(
provider.Object,
detailsProvider,
new DefaultMetadataDetails(
ModelMetadataIdentity.ForProperty(typeof(int), "Prop2", typeof(string)),
ModelMetadataIdentity.ForProperty(prop2, typeof(int), typeof(string)),
attributes: new ModelAttributes(new object[0], new object[0], null))),
};
@ -475,7 +478,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
provider.Object,
detailsProvider,
new DefaultMetadataDetails(
#pragma warning disable CS0618 // Using the obsolete overload does not affect the intent of this test, but fixing it requires a lot of code churn.
ModelMetadataIdentity.ForProperty(typeof(int), originalName, typeof(string)),
#pragma warning restore CS0618 // Type or member is obsolete
attributes: new ModelAttributes(new object[0], new object[0], null))));
}
@ -575,7 +580,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
foreach (var kvp in originalNamesAndOrders)
{
var propertyCache = new DefaultMetadataDetails(
#pragma warning disable CS0618 // Using the obsolete overload does not affect the intent of this test, but fixing it requires a lot of code churn.
ModelMetadataIdentity.ForProperty(typeof(int), kvp.Key, typeof(string)),
#pragma warning restore CS0618 // Type or member is obsolete
attributes: new ModelAttributes(new object[0], new object[0], null))
{
DisplayMetadata = new DisplayMetadata(),
@ -934,7 +941,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
// Arrange
var property = GetType()
.GetProperty(nameof(CalculateHasValidators_PropertyMetadata_TypeHasNoValidatorsProperty), BindingFlags.Static | BindingFlags.NonPublic);
var modelIdentity = ModelMetadataIdentity.ForProperty(property.PropertyType, property.Name, GetType());
var modelIdentity = ModelMetadataIdentity.ForProperty(property, property.PropertyType, GetType());
var modelMetadata = CreateModelMetadata(modelIdentity, Mock.Of<IModelMetadataProvider>(), false);
// Act
@ -997,7 +1004,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var propertyIdentity = ModelMetadataIdentity.ForProperty(typeof(int), nameof(TypeWithProperties.PublicGetPublicSetProperty), typeof(string));
var property = typeof(TypeWithProperties).GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty));
var propertyIdentity = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(TypeWithProperties));
var propertyMetadata = new Mock<ModelMetadata>(propertyIdentity);
metadataProvider
@ -1021,10 +1029,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var property1Identity = ModelMetadataIdentity.ForProperty(typeof(int), nameof(TypeWithProperties.PublicGetPublicSetProperty), typeof(string));
var property1Identity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)), typeof(int), modelType);
var property1Metadata = CreateModelMetadata(property1Identity, metadataProvider.Object, false);
var property2Identity = ModelMetadataIdentity.ForProperty(typeof(int), nameof(TypeWithProperties.PublicGetProtectedSetProperty), typeof(string));
var property2Identity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetProtectedSetProperty)), typeof(int), modelType);
var property2Metadata = CreateModelMetadata(property2Identity, metadataProvider.Object, true);
metadataProvider
@ -1048,7 +1056,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var propertyIdentity = ModelMetadataIdentity.ForProperty(typeof(int), nameof(TypeWithProperties.PublicGetPublicSetProperty), typeof(string));
var propertyIdentity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)), typeof(int), modelType);
var propertyMetadata = CreateModelMetadata(propertyIdentity, metadataProvider.Object, null);
metadataProvider
@ -1072,10 +1080,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var property1Identity = ModelMetadataIdentity.ForProperty(typeof(int), nameof(TypeWithProperties.PublicGetPublicSetProperty), modelType);
var property1Identity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetPublicSetProperty)), typeof(int), modelType);
var property1Metadata = CreateModelMetadata(property1Identity, metadataProvider.Object, false);
var property2Identity = ModelMetadataIdentity.ForProperty(typeof(int), nameof(TypeWithProperties.PublicGetProtectedSetProperty), modelType);
var property2Identity = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(TypeWithProperties.PublicGetProtectedSetProperty)), typeof(int), modelType);
var property2Metadata = CreateModelMetadata(property2Identity, metadataProvider.Object, false);
metadataProvider
@ -1099,18 +1107,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var employeeId = ModelMetadataIdentity.ForProperty(typeof(int), nameof(Employee.Id), modelType);
var employeeId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Id)), typeof(int), modelType);
var employeeIdMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var employeeUnit = ModelMetadataIdentity.ForProperty(typeof(BusinessUnit), nameof(Employee.Unit), modelType);
var employeeUnit = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Unit)), typeof(BusinessUnit), modelType);
var employeeUnitMetadata = CreateModelMetadata(employeeUnit, metadataProvider.Object, false);
var employeeManager = ModelMetadataIdentity.ForProperty(typeof(Employee), nameof(Employee.Unit), modelType);
var employeeManager = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Manager)), typeof(Employee), modelType);
var employeeManagerMetadata = CreateModelMetadata(employeeManager, metadataProvider.Object, false);
var employeeEmployees = ModelMetadataIdentity.ForProperty(typeof(List<Employee>), nameof(Employee.Employees), modelType);
var employeeEmployees = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Employees)), typeof(List<Employee>), modelType);
var employeeEmployeesMetadata = CreateModelMetadata(employeeEmployees, metadataProvider.Object, false);
var unitHead = ModelMetadataIdentity.ForProperty(typeof(Employee), nameof(BusinessUnit.Head), modelType);
var unitModel = typeof(BusinessUnit);
var unitHead = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Head)), typeof(Employee), unitModel);
var unitHeadMetadata = CreateModelMetadata(unitHead, metadataProvider.Object, false);
var unitId = ModelMetadataIdentity.ForProperty(typeof(int), nameof(BusinessUnit.Id), modelType);
var unitId = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Id)), typeof(int), unitModel);
var unitIdMetadata = CreateModelMetadata(unitId, metadataProvider.Object, true); // BusinessUnit.Id has validators.
metadataProvider
@ -1139,18 +1148,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var employeeId = ModelMetadataIdentity.ForProperty(typeof(int), nameof(Employee.Id), modelType);
var employeeId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Id)), typeof(int), modelType);
var employeeIdMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var employeeUnit = ModelMetadataIdentity.ForProperty(typeof(BusinessUnit), nameof(Employee.Unit), modelType);
var employeeUnit = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Unit)), typeof(BusinessUnit), modelType);
var employeeUnitMetadata = CreateModelMetadata(employeeUnit, metadataProvider.Object, false);
var employeeManager = ModelMetadataIdentity.ForProperty(typeof(Employee), nameof(Employee.Unit), modelType);
var employeeManager = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Manager)), typeof(Employee), modelType);
var employeeManagerMetadata = CreateModelMetadata(employeeManager, metadataProvider.Object, false);
var employeeEmployees = ModelMetadataIdentity.ForProperty(typeof(List<Employee>), nameof(Employee.Employees), modelType);
var employeeEmployees = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Employees)), typeof(List<Employee>), modelType);
var employeeEmployeesMetadata = CreateModelMetadata(employeeEmployees, metadataProvider.Object, false);
var unitHead = ModelMetadataIdentity.ForProperty(typeof(Employee), nameof(BusinessUnit.Head), modelType);
var unitModel = typeof(BusinessUnit);
var unitHead = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Head)), typeof(Employee), unitModel);
var unitHeadMetadata = CreateModelMetadata(unitHead, metadataProvider.Object, true); // BusinessUnit.Head has validators
var unitId = ModelMetadataIdentity.ForProperty(typeof(int), nameof(BusinessUnit.Id), modelType);
var unitId = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Id)), typeof(int), unitModel);
var unitIdMetadata = CreateModelMetadata(unitId, metadataProvider.Object, false);
metadataProvider
@ -1181,9 +1191,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var employeeId = ModelMetadataIdentity.ForProperty(typeof(int), nameof(Employee.Id), modelType);
var employeeId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Id)), typeof(int), modelType);
var employeeIdMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var employeeEmployees = ModelMetadataIdentity.ForProperty(typeof(List<Employee>), nameof(Employee.Employees), modelType);
var employeeEmployees = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Employees)), typeof(List<Employee>), modelType);
var employeeEmployeesMetadata = CreateModelMetadata(employeeEmployees, metadataProvider.Object, false);
metadataProvider
@ -1210,18 +1220,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var metadataProvider = new Mock<IModelMetadataProvider>();
var modelMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var employeeId = ModelMetadataIdentity.ForProperty(typeof(int), nameof(Employee.Id), modelType);
var employeeId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Id)), typeof(int), modelType);
var employeeIdMetadata = CreateModelMetadata(modelIdentity, metadataProvider.Object, false);
var employeeUnit = ModelMetadataIdentity.ForProperty(typeof(BusinessUnit), nameof(Employee.Unit), modelType);
var employeeUnit = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Unit)), typeof(BusinessUnit), modelType);
var employeeUnitMetadata = CreateModelMetadata(employeeUnit, metadataProvider.Object, false);
var employeeManager = ModelMetadataIdentity.ForProperty(typeof(Employee), nameof(Employee.Unit), modelType);
var employeeManager = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Manager)), typeof(Employee), modelType);
var employeeManagerMetadata = CreateModelMetadata(employeeManager, metadataProvider.Object, false);
var employeeEmployeesId = ModelMetadataIdentity.ForProperty(typeof(List<Employee>), nameof(Employee.Employees), modelType);
var employeeEmployeesId = ModelMetadataIdentity.ForProperty(modelType.GetProperty(nameof(Employee.Employees)), typeof(List<Employee>), modelType);
var employeeEmployeesIdMetadata = CreateModelMetadata(employeeEmployeesId, metadataProvider.Object, false);
var unitHead = ModelMetadataIdentity.ForProperty(typeof(Employee), nameof(BusinessUnit.Head), modelType);
var unitModel = typeof(BusinessUnit);
var unitHead = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Head)), typeof(Employee), unitModel);
var unitHeadMetadata = CreateModelMetadata(unitHead, metadataProvider.Object, false);
var unitId = ModelMetadataIdentity.ForProperty(typeof(int), nameof(BusinessUnit.Id), modelType);
var unitId = ModelMetadataIdentity.ForProperty(unitModel.GetProperty(nameof(BusinessUnit.Id)), typeof(int), unitModel);
var unitIdMetadata = CreateModelMetadata(unitId, metadataProvider.Object, false);
metadataProvider

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var provider = new DefaultValidationMetadataProvider();
var attributes = new Attribute[] { new ValidateNeverAttribute() };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var key = ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
// Act
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var provider = new DefaultValidationMetadataProvider();
var attributes = new Attribute[] { new ValidateNeverAttribute() };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var key = ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(attributes, new object[0], null));
// Act
@ -71,8 +71,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var provider = new DefaultValidationMetadataProvider();
var key = ModelMetadataIdentity.ForProperty(
typeof(ValidateNeverClass).GetProperty(nameof(ValidateNeverClass.ClassName)),
typeof(string),
nameof(ValidateNeverClass.ClassName),
typeof(ValidateNeverClass));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0], null));
@ -93,8 +93,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var provider = new DefaultValidationMetadataProvider();
var key = ModelMetadataIdentity.ForProperty(
typeof(ValidateNeverSubclass).GetProperty(nameof(ValidateNeverSubclass.SubclassName)),
typeof(string),
nameof(ValidateNeverSubclass.SubclassName),
typeof(ValidateNeverSubclass));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0], null));
@ -116,7 +116,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var attribute = new TestClientModelValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var key = ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
// Act
@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var attribute = new TestModelValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var key = ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
// Act
@ -154,7 +154,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var attribute = new TestValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var key = ModelMetadataIdentity.ForProperty(typeof(string).GetProperty(nameof(string.Length)), typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
context.ValidationMetadata.ValidatorMetadata.Add(attribute);

View File

@ -16,8 +16,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var provider = new ExcludeBindingMetadataProvider(typeof(string));
var key = ModelMetadataIdentity.ForProperty(
typeof(Person).GetProperty(nameof(Person.Age)),
typeof(int),
nameof(Person.Age),
typeof(Person));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0], null));
@ -40,8 +40,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var provider = new ExcludeBindingMetadataProvider(typeof(int));
var key = ModelMetadataIdentity.ForProperty(
typeof(Person).GetProperty(nameof(Person.Age)),
typeof(int),
nameof(Person.Age),
typeof(Person));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0], null));

View File

@ -365,12 +365,29 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
// Do nothing.
}
else if (context.Key.MetadataKind == ModelMetadataKind.Property)
{
var property = context.Key.PropertyInfo;
if (property is null)
{
// PropertyInfo was unavailable on ModelIdentity prior to 3.1.
// Making a cogent argument about the nullability of the property requires inspecting the declared type,
// since looking at the runtime type may result in false positives: https://github.com/aspnet/AspNetCore/issues/14812
// The only way we could arrive here is if the ModelMetadata was constructed using the non-default provider.
// We'll cursorily examine the attributes on the property, but not the ContainerType to make a decision about it's nullability.
if (HasNullableAttribute(context.PropertyAttributes, out var propertyHasNullableAttribute))
{
addInferredRequiredAttribute = propertyHasNullableAttribute;
}
}
else
{
addInferredRequiredAttribute = IsNullableReferenceType(
context.Key.ContainerType,
property.DeclaringType,
member: null,
context.PropertyAttributes);
}
}
else if (context.Key.MetadataKind == ModelMetadataKind.Parameter)
{
addInferredRequiredAttribute = IsNullableReferenceType(

View File

@ -599,13 +599,13 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
provider.CreateDisplayMetadata(context);
// Assert
using(new CultureReplacer("en-US", "en-US"))
using (new CultureReplacer("en-US", "en-US"))
{
Assert.Equal("name from localizer en-US", context.DisplayMetadata.DisplayName());
Assert.Equal("description from localizer en-US", context.DisplayMetadata.Description());
Assert.Equal("prompt from localizer en-US", context.DisplayMetadata.Placeholder());
}
using(new CultureReplacer("fr-FR", "fr-FR"))
using (new CultureReplacer("fr-FR", "fr-FR"))
{
Assert.Equal("name from localizer fr-FR", context.DisplayMetadata.DisplayName());
Assert.Equal("description from localizer fr-FR", context.DisplayMetadata.Description());
@ -1031,12 +1031,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
// Assert
var groupTwo = Assert.Single(enumNameAndGroup, e => e.Value.Equals("2", StringComparison.Ordinal));
using(new CultureReplacer("en-US", "en-US"))
using (new CultureReplacer("en-US", "en-US"))
{
Assert.Equal("Loc_Two_Name", groupTwo.Key.Name);
}
using(new CultureReplacer("fr-FR", "fr-FR"))
using (new CultureReplacer("fr-FR", "fr-FR"))
{
Assert.Equal("Loc_Two_Name", groupTwo.Key.Name);
}
@ -1051,12 +1051,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
// Assert
var groupTwo = Assert.Single(enumNameAndGroup, e => e.Value.Equals("2", StringComparison.Ordinal));
using(new CultureReplacer("en-US", "en-US"))
using (new CultureReplacer("en-US", "en-US"))
{
Assert.Equal("Loc_Two_Name en-US", groupTwo.Key.Name);
}
using(new CultureReplacer("fr-FR", "fr-FR"))
using (new CultureReplacer("fr-FR", "fr-FR"))
{
Assert.Equal("Loc_Two_Name fr-FR", groupTwo.Key.Name);
}
@ -1071,12 +1071,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
// Assert
var groupThree = Assert.Single(enumNameAndGroup, e => e.Value.Equals("3", StringComparison.Ordinal));
using(new CultureReplacer("en-US", "en-US"))
using (new CultureReplacer("en-US", "en-US"))
{
Assert.Equal("type three name en-US", groupThree.Key.Name);
}
using(new CultureReplacer("fr-FR", "fr-FR"))
using (new CultureReplacer("fr-FR", "fr-FR"))
{
Assert.Equal("type three name fr-FR", groupThree.Key.Name);
}
@ -1091,12 +1091,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var groupThree = Assert.Single(enumNameAndGroup, e => e.Value.Equals("3", StringComparison.Ordinal));
// Assert
using(new CultureReplacer("en-US", "en-US"))
using (new CultureReplacer("en-US", "en-US"))
{
Assert.Equal("type three name en-US", groupThree.Key.Name);
}
using(new CultureReplacer("fr-FR", "fr-FR"))
using (new CultureReplacer("fr-FR", "fr-FR"))
{
Assert.Equal("type three name fr-FR", groupThree.Key.Name);
}
@ -1111,7 +1111,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var required = new RequiredAttribute();
var attributes = new Attribute[] { required };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var key = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
// Act
@ -1131,7 +1132,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var provider = CreateProvider();
var attributes = new Attribute[] { };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var key = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.ValidationMetadata.IsRequired = initialValue;
@ -1152,8 +1154,9 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
typeof(NullableReferenceTypes),
typeof(NullableReferenceTypes).GetProperty(nameof(NullableReferenceTypes.NonNullableReferenceType)));
var key = ModelMetadataIdentity.ForProperty(
typeof(NullableReferenceTypes),
nameof(NullableReferenceTypes.NonNullableReferenceType), typeof(string));
typeof(NullableReferenceTypes).GetProperty(nameof(NullableReferenceTypes.NonNullableReferenceType)),
typeof(string),
typeof(NullableReferenceTypes));
var context = new ValidationMetadataProviderContext(key, attributes);
// Act
@ -1174,9 +1177,11 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var attributes = ModelAttributes.GetAttributesForProperty(
typeof(NullableReferenceTypes),
typeof(NullableReferenceTypes).GetProperty(nameof(NullableReferenceTypes.NonNullableReferenceTypeWithRequired)));
var key = ModelMetadataIdentity.ForProperty(
typeof(NullableReferenceTypes),
nameof(NullableReferenceTypes.NonNullableReferenceTypeWithRequired), typeof(string));
typeof(NullableReferenceTypes).GetProperty(nameof(NullableReferenceTypes.NonNullableReferenceTypeWithRequired)),
typeof(string),
typeof(NullableReferenceTypes));
var context = new ValidationMetadataProviderContext(key, attributes);
// Act
@ -1201,9 +1206,12 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var attributes = ModelAttributes.GetAttributesForProperty(
typeof(NullableReferenceTypes),
typeof(NullableReferenceTypes).GetProperty(nameof(NullableReferenceTypes.NonNullableReferenceType)));
var key = ModelMetadataIdentity.ForProperty(
typeof(NullableReferenceTypes),
nameof(NullableReferenceTypes.NonNullableReferenceType), typeof(string));
typeof(NullableReferenceTypes).GetProperty(nameof(NullableReferenceTypes.NonNullableReferenceType)),
typeof(string),
typeof(NullableReferenceTypes));
var context = new ValidationMetadataProviderContext(key, attributes);
// Act
@ -1214,6 +1222,189 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
Assert.DoesNotContain(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Theory]
[InlineData(nameof(DerivedTypeWithAllNonNullProperties.Property1))]
[InlineData(nameof(DerivedTypeWithAllNonNullProperties.Property2))]
public void CreateValidationMetadata_InfersRequiredAttributeOnDerivedType_BaseAnDerivedTypHaveAllNonNullProperties(string propertyName)
{
// Arrange
var provider = CreateProvider();
var modelType = typeof(DerivedTypeWithAllNonNullProperties);
var property = modelType.GetProperty(propertyName);
var key = ModelMetadataIdentity.ForProperty(property, property.PropertyType, modelType);
var context = new ValidationMetadataProviderContext(key, ModelAttributes.GetAttributesForProperty(modelType, property));
// This test verifies how MVC reads the NullableContextOptions. We expect the property to not have a Nullable attribute on, and for
// the types to have NullableContext. We'll encode our expectations as assertions so that we can catch if or when the compiler changes
// this behavior and the test needs to be tweaked.
Assert.False(DataAnnotationsMetadataProvider.HasNullableAttribute(context.PropertyAttributes, out _), "We do not expect NullableAttribute to be defined on the property");
// Act
provider.CreateValidationMetadata(context);
// Assert
Assert.True(context.ValidationMetadata.IsRequired);
Assert.Contains(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Fact]
public void CreateValidationMetadata_InfersRequiredAttributeOnDerivedType_PropertyDeclaredOnBaseType()
{
// Arrange
var provider = CreateProvider();
var modelType = typeof(DerivedTypeWithAllNonNullProperties_WithNullableProperties);
var property = modelType.GetProperty(nameof(DerivedTypeWithAllNonNullProperties_WithNullableProperties.Property1));
var key = ModelMetadataIdentity.ForProperty(property, property.PropertyType, modelType);
var context = new ValidationMetadataProviderContext(key, ModelAttributes.GetAttributesForProperty(modelType, property));
// Act
provider.CreateValidationMetadata(context);
// Assert
Assert.True(context.ValidationMetadata.IsRequired);
Assert.Contains(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Fact]
public void CreateValidationMetadata_InfersRequiredAttributeOnDerivedType_NullablePropertyDeclaredOnDerviedType()
{
// Arrange
var provider = CreateProvider();
var modelType = typeof(DerivedTypeWithAllNonNullProperties_WithNullableProperties);
var property = modelType.GetProperty(nameof(DerivedTypeWithAllNonNullProperties_WithNullableProperties.Property2));
var key = ModelMetadataIdentity.ForProperty(property, property.PropertyType, modelType);
var context = new ValidationMetadataProviderContext(key, ModelAttributes.GetAttributesForProperty(modelType, property));
// Act
provider.CreateValidationMetadata(context);
// Assert
Assert.Null(context.ValidationMetadata.IsRequired);
Assert.DoesNotContain(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Theory]
[InlineData(nameof(DerivedTypeWithNullableProperties.Property1))]
[InlineData(nameof(DerivedTypeWithNullableProperties.Property2))]
public void CreateValidationMetadata_BaseAnDerivedTypHaveAllNullableProperties_DoesNotInferRequiredAttribute(string propertyName)
{
// Arrange
var provider = CreateProvider();
var modelType = typeof(DerivedTypeWithNullableProperties);
var property = modelType.GetProperty(propertyName);
var key = ModelMetadataIdentity.ForProperty(property, property.PropertyType, modelType);
var context = new ValidationMetadataProviderContext(key, ModelAttributes.GetAttributesForProperty(modelType, property));
// Act
provider.CreateValidationMetadata(context);
// Assert
Assert.Null(context.ValidationMetadata.IsRequired);
Assert.DoesNotContain(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Fact]
public void CreateValidationMetadata_InfersRequiredAttribute_BaseTypeIsNullable_PropertyIsNotNull()
{
// Tests the scenario listed in https://github.com/aspnet/AspNetCore/issues/14812
// Arrange
var provider = CreateProvider();
var modelType = typeof(DerivedTypeWithNullableProperties_WithNonNullProperties);
var property = modelType.GetProperty(nameof(DerivedTypeWithNullableProperties_WithNonNullProperties.Property2));
var key = ModelMetadataIdentity.ForProperty(property, property.PropertyType, modelType);
var context = new ValidationMetadataProviderContext(key, ModelAttributes.GetAttributesForProperty(modelType, property));
// Act
provider.CreateValidationMetadata(context);
// Assert
Assert.True(context.ValidationMetadata.IsRequired);
Assert.Contains(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Fact]
public void CreateValidationMetadata_InfersRequiredAttribute_ShadowedPropertyIsNonNull()
{
// Arrange
var provider = CreateProvider();
var modelType = typeof(DerivedTypeWithNullableProperties_ShadowedProperty);
var property = modelType.GetProperty(nameof(DerivedTypeWithNullableProperties_ShadowedProperty.Property1));
var key = ModelMetadataIdentity.ForProperty(property, property.PropertyType, modelType);
var context = new ValidationMetadataProviderContext(key, ModelAttributes.GetAttributesForProperty(modelType, property));
// Act
provider.CreateValidationMetadata(context);
// Assert
Assert.True(context.ValidationMetadata.IsRequired);
Assert.Contains(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Fact]
public void CreateValidationMetadata_DoesNotInfersRequiredAttribute_TypeImplementingNonNullAbstractClass()
{
// Arrange
var provider = CreateProvider();
var modelType = typeof(TypeImplementIInterfaceWithNonNullProperty);
var property = modelType.GetProperty(nameof(TypeImplementIInterfaceWithNonNullProperty.Property));
var key = ModelMetadataIdentity.ForProperty(property, property.PropertyType, modelType);
var context = new ValidationMetadataProviderContext(key, ModelAttributes.GetAttributesForProperty(modelType, property));
// Act
provider.CreateValidationMetadata(context);
// Assert
Assert.True(context.ValidationMetadata.IsRequired);
Assert.Contains(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Fact]
public void CreateValidationMetadata_DoesNotInfersRequiredAttribute_TypeImplementingNonNullAbstractClass_NotNullable()
{
// Arrange
var provider = CreateProvider();
var modelType = typeof(TypeImplementIInterfaceWithNonNullProperty_AsNullable);
var property = modelType.GetProperty(nameof(TypeImplementIInterfaceWithNonNullProperty_AsNullable.Property));
var key = ModelMetadataIdentity.ForProperty(property, property.PropertyType, modelType);
var context = new ValidationMetadataProviderContext(key, ModelAttributes.GetAttributesForProperty(modelType, property));
// Act
provider.CreateValidationMetadata(context);
// Assert
Assert.Null(context.ValidationMetadata.IsRequired);
Assert.DoesNotContain(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Fact]
public void CreateValidationMetadata_WithOldModelIdentity_DoesNotInferValueBasedOnContext()
{
// Arrange
var provider = CreateProvider();
var modelType = typeof(TypeWithAllNonNullProperties);
var property = modelType.GetProperty(nameof(TypeWithAllNonNullProperties.Property1));
#pragma warning disable CS0618 // Type or member is obsolete
var key = ModelMetadataIdentity.ForProperty(property.PropertyType, property.Name, modelType);
#pragma warning restore CS0618 // Type or member is obsolete
var context = new ValidationMetadataProviderContext(key, ModelAttributes.GetAttributesForProperty(modelType, property));
// Act
provider.CreateValidationMetadata(context);
// Assert
Assert.Null(context.ValidationMetadata.IsRequired);
Assert.DoesNotContain(context.ValidationMetadata.ValidatorMetadata, m => m is RequiredAttribute);
}
[Fact]
public void CreateValidationMetadata_WillAddValidationAttributes_From_ValidationProviderAttribute()
{
@ -1227,7 +1418,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
});
var attributes = new Attribute[] { new EmailAddressAttribute(), validationProviderAttribute };
var key = ModelMetadataIdentity.ForProperty(typeof(string), "Length", typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var key = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
// Act
@ -1254,7 +1446,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var provider = CreateProvider();
var attributes = new Attribute[] { new RequiredAttribute() };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var key = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(string));
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.BindingMetadata.IsBindingRequired = initialValue;
@ -1275,7 +1468,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var provider = CreateProvider();
var attributes = new Attribute[] { };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var key = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(string));
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.BindingMetadata.IsReadOnly = initialValue;
@ -1294,7 +1488,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var attribute = new TestValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var key = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
// Act
@ -1313,7 +1508,29 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var attribute = new TestValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var property = typeof(string).GetProperty(nameof(string.Length));
var key = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.ValidationMetadata.ValidatorMetadata.Add(attribute);
// Act
provider.CreateValidationMetadata(context);
// Assert
var validatorMetadata = Assert.Single(context.ValidationMetadata.ValidatorMetadata);
Assert.Same(attribute, validatorMetadata);
}
[Fact]
public void CreateValidationDetails_ForProperty()
{
// Arrange
var provider = CreateProvider();
var attribute = new TestValidationAttribute();
var attributes = new Attribute[] { attribute };
var property = typeof(string).GetProperty(nameof(string.Length));
var key = ModelMetadataIdentity.ForProperty(property, typeof(int), typeof(string));
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.ValidationMetadata.ValidatorMetadata.Add(attribute);
@ -1479,7 +1696,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
public bool Equals(KeyValuePair<EnumGroupAndName, string> x, KeyValuePair<EnumGroupAndName, string> y)
{
using(new CultureReplacer(string.Empty, string.Empty))
using (new CultureReplacer(string.Empty, string.Empty))
{
return x.Key.Name.Equals(y.Key.Name, StringComparison.Ordinal)
&& x.Key.Group.Equals(y.Key.Group, StringComparison.Ordinal);
@ -1488,7 +1705,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
public int GetHashCode(KeyValuePair<EnumGroupAndName, string> obj)
{
using(new CultureReplacer(string.Empty, string.Empty))
using (new CultureReplacer(string.Empty, string.Empty))
{
return obj.Key.GetHashCode();
}
@ -1657,6 +1874,56 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
{
}
}
private class TypeWithAllNonNullProperties
{
public string Property1 { get; set; } = string.Empty;
}
private class DerivedTypeWithAllNonNullProperties : TypeWithAllNonNullProperties
{
public string Property2 { get; set; } = string.Empty;
}
private class DerivedTypeWithAllNonNullProperties_WithNullableProperties : TypeWithAllNonNullProperties
{
public string? Property2 { get; set; } = string.Empty;
}
private class TypeWithNullableProperties
{
public string? Property1 { get; set; }
}
private class DerivedTypeWithNullableProperties : TypeWithNullableProperties
{
public string? Property2 { get; set; }
}
private class DerivedTypeWithNullableProperties_WithNonNullProperties : TypeWithNullableProperties
{
public string Property2 { get; set; } = string.Empty;
}
private class DerivedTypeWithNullableProperties_ShadowedProperty : TypeWithNullableProperties
{
public new string Property1 { get; set; } = string.Empty;
}
public abstract class AbstraceTypehNonNullProperty
{
public abstract string Property { get; set; }
}
public class TypeImplementIInterfaceWithNonNullProperty : AbstraceTypehNonNullProperty
{
public override string Property { get; set; } = string.Empty;
}
#nullable restore
public class TypeImplementIInterfaceWithNonNullProperty_AsNullable : AbstraceTypehNonNullProperty
{
public override string Property { get; set; }
}
}
}

View File

@ -24,8 +24,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
};
var key = ModelMetadataIdentity.ForProperty(
typeof(ClassWithDataMemberIsRequiredTrue).GetProperty(nameof(ClassWithDataMemberIsRequiredTrue.StringProperty)),
typeof(string),
nameof(ClassWithDataMemberIsRequiredTrue.StringProperty),
typeof(ClassWithDataMemberIsRequiredTrue));
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
@ -50,8 +50,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
};
var key = ModelMetadataIdentity.ForProperty(
typeof(ClassWithDataMemberIsRequiredFalse).GetProperty(nameof(ClassWithDataMemberIsRequiredFalse.StringProperty)),
typeof(string),
nameof(ClassWithDataMemberIsRequiredFalse.StringProperty),
typeof(ClassWithDataMemberIsRequiredFalse));
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
@ -98,8 +98,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var provider = new DataMemberRequiredBindingMetadataProvider();
var key = ModelMetadataIdentity.ForProperty(
typeof(ClassWithoutAttributes).GetProperty(nameof(ClassWithoutAttributes.StringProperty)),
typeof(string),
nameof(ClassWithoutAttributes.StringProperty),
typeof(ClassWithoutAttributes));
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], new object[0]));
@ -126,8 +126,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
};
var key = ModelMetadataIdentity.ForProperty(
typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract).GetProperty(nameof(ClassWithDataMemberIsRequiredTrueWithoutDataContract.StringProperty)),
typeof(string),
nameof(ClassWithDataMemberIsRequiredTrueWithoutDataContract.StringProperty),
typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract));
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));

View File

@ -196,8 +196,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
}
if (!(exception is JsonException || exception is OverflowException))
if (!(exception is JsonException || exception is OverflowException || exception is FormatException))
{
// At this point we've already recorded all exceptions as an entry in the ModelStateDictionary.
// We only need to rethrow an exception if we believe it needs to be handled by something further up
// the stack.
// JsonException, OverflowException, and FormatException are assumed to be only encountered when
// parsing the JSON and are consequently "safe" to be exposed as part of ModelState. Everything else
// needs to be rethrown.
var exceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception);
exceptionDispatchInfo.Throw();
}

View File

@ -14,6 +14,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool;
using Moq;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Xunit;
@ -21,8 +22,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
public class NewtonsoftJsonInputFormatterTest : JsonInputFormatterTestBase
{
private static readonly ObjectPoolProvider _objectPoolProvider = new DefaultObjectPoolProvider();
private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings();
private readonly ObjectPoolProvider _objectPoolProvider = new DefaultObjectPoolProvider();
private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings();
[Fact]
public async Task Constructor_BuffersRequestBody_UsingDefaultOptions()
@ -144,7 +145,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
var serializerSettings = new JsonSerializerSettings();
// Act
var formatter = new TestableJsonInputFormatter(serializerSettings);
var formatter = new TestableJsonInputFormatter(serializerSettings, _objectPoolProvider);
// Assert
Assert.Same(serializerSettings, formatter.SerializerSettings);
@ -185,7 +186,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
MaxDepth = 2,
DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind,
};
var formatter = new TestableJsonInputFormatter(settings);
var formatter = new TestableJsonInputFormatter(settings, _objectPoolProvider);
// Act
var actual = formatter.CreateJsonSerializer(null);
@ -304,7 +305,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
[Fact]
public async Task ReadAsync_lowInputFormatterExceptionMessages_DoesNotWrapJsonInputExceptions()
public async Task ReadAsync_AllowInputFormatterExceptionMessages_DoesNotWrapJsonInputExceptions()
{
// Arrange
var formatter = new NewtonsoftJsonInputFormatter(
@ -336,10 +337,72 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
Assert.NotEmpty(modelError.ErrorMessage);
}
[Fact]
public async Task ReadAsync_DoesNotRethrowFormatExceptions()
{
// Arrange
_serializerSettings.Converters.Add(new IsoDateTimeConverter());
var formatter = new NewtonsoftJsonInputFormatter(
GetLogger(),
_serializerSettings,
ArrayPool<char>.Shared,
_objectPoolProvider,
new MvcOptions(),
new MvcNewtonsoftJsonOptions());
var contentBytes = Encoding.UTF8.GetBytes("{\"dateValue\":\"not-a-date\"}");
var httpContext = GetHttpContext(contentBytes);
var formatterContext = CreateInputFormatterContext(typeof(TypeWithPrimitives), httpContext);
// Act
var result = await formatter.ReadAsync(formatterContext);
// Assert
Assert.True(result.HasError);
Assert.False(formatterContext.ModelState.IsValid);
var modelError = Assert.Single(formatterContext.ModelState["dateValue"].Errors);
Assert.Null(modelError.Exception);
Assert.Equal("The supplied value is invalid.", modelError.ErrorMessage);
}
[Fact]
public async Task ReadAsync_DoesNotRethrowOverflowExceptions()
{
// Arrange
_serializerSettings.Converters.Add(new IsoDateTimeConverter());
var formatter = new NewtonsoftJsonInputFormatter(
GetLogger(),
_serializerSettings,
ArrayPool<char>.Shared,
_objectPoolProvider,
new MvcOptions(),
new MvcNewtonsoftJsonOptions());
var contentBytes = Encoding.UTF8.GetBytes("{\"shortValue\":\"32768\"}");
var httpContext = GetHttpContext(contentBytes);
var formatterContext = CreateInputFormatterContext(typeof(TypeWithPrimitives), httpContext);
// Act
var result = await formatter.ReadAsync(formatterContext);
// Assert
Assert.True(result.HasError);
Assert.False(formatterContext.ModelState.IsValid);
var modelError = Assert.Single(formatterContext.ModelState["shortValue"].Errors);
Assert.Null(modelError.Exception);
Assert.Equal("The supplied value is invalid.", modelError.ErrorMessage);
}
private class TestableJsonInputFormatter : NewtonsoftJsonInputFormatter
{
public TestableJsonInputFormatter(JsonSerializerSettings settings)
: base(GetLogger(), settings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions(), new MvcNewtonsoftJsonOptions())
public TestableJsonInputFormatter(JsonSerializerSettings settings, ObjectPoolProvider objectPoolProvider)
: base(GetLogger(), settings, ArrayPool<char>.Shared, objectPoolProvider, new MvcOptions(), new MvcNewtonsoftJsonOptions())
{
}
@ -418,5 +481,26 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
[JsonProperty(Required = Required.Always)]
public string Password { get; set; }
}
public class TypeWithPrimitives
{
public DateTime DateValue { get; set; }
[JsonConverter(typeof(IncorrectShortConverter))]
public short ShortValue { get; set; }
}
private class IncorrectShortConverter : JsonConverter<short>
{
public override short ReadJson(JsonReader reader, Type objectType, short existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return short.Parse(reader.Value.ToString());
}
public override void WriteJson(JsonWriter writer, short value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
}

View File

@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var property = containerType.GetRuntimeProperty(propertyName);
Assert.NotNull(property);
var key = ModelMetadataIdentity.ForProperty(property.PropertyType, propertyName, containerType);
var key = ModelMetadataIdentity.ForProperty(property, property.PropertyType, containerType);
var builder = new MetadataBuilder(key);
_detailsProvider.Builders.Add(builder);

View File

@ -5,7 +5,7 @@
</div>
<div class="main">
<div class="top-row px-4">
<div class="top-row px-4 auth">
<LoginDisplay />
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>

View File

@ -1,4 +1,5 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",

View File

@ -30,66 +30,72 @@ app {
flex: 1;
}
.main .top-row {
.main .top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
}
}
.main .top-row > a {
.main .top-row > a, .main .top-row .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
}
.main .top-row a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.sidebar .top-row {
.sidebar .top-row {
background-color: rgba(0,0,0,0.4);
}
}
.sidebar .navbar-brand {
.sidebar .navbar-brand {
font-size: 1.1rem;
}
}
.sidebar .oi {
.sidebar .oi {
width: 2rem;
font-size: 1.1rem;
vertical-align: text-top;
top: -2px;
}
}
.nav-item {
.sidebar .nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
}
.nav-item:first-of-type {
.sidebar .nav-item:first-of-type {
padding-top: 1rem;
}
}
.nav-item:last-of-type {
.sidebar .nav-item:last-of-type {
padding-bottom: 1rem;
}
}
.nav-item a {
.sidebar .nav-item a {
color: #d7d7d7;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
}
}
.nav-item a.active {
.sidebar .nav-item a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
}
.nav-item a:hover {
.sidebar .nav-item a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
}
.content {
padding-top: 1.1rem;
@ -131,9 +137,17 @@ app {
}
@media (max-width: 767.98px) {
.main .top-row {
.main .top-row:not(.auth) {
display: none;
}
.main .top-row.auth {
justify-content: space-between;
}
.main .top-row a, .main .top-row .btn-link {
margin-left: 0;
}
}
@media (min-width: 768px) {

View File

@ -10,10 +10,10 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
public static readonly string DisplayName = "Facebook";
// https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login
public static readonly string AuthorizationEndpoint = "https://www.facebook.com/v3.3/dialog/oauth";
public static readonly string AuthorizationEndpoint = "https://www.facebook.com/v4.0/dialog/oauth";
public static readonly string TokenEndpoint = "https://graph.facebook.com/v3.3/oauth/access_token";
public static readonly string TokenEndpoint = "https://graph.facebook.com/v4.0/oauth/access_token";
public static readonly string UserInformationEndpoint = "https://graph.facebook.com/v3.3/me";
public static readonly string UserInformationEndpoint = "https://graph.facebook.com/v4.0/me";
}
}

View File

@ -225,7 +225,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
var transaction = await server.SendAsync("http://example.com/base/login");
Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode);
var location = transaction.Response.Headers.Location.AbsoluteUri;
Assert.Contains("https://www.facebook.com/v3.3/dialog/oauth", location);
Assert.Contains("https://www.facebook.com/v4.0/dialog/oauth", location);
Assert.Contains("response_type=code", location);
Assert.Contains("client_id=", location);
Assert.Contains("redirect_uri=" + UrlEncoder.Default.Encode("http://example.com/base/signin-facebook"), location);
@ -257,7 +257,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
var transaction = await server.SendAsync("http://example.com/login");
Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode);
var location = transaction.Response.Headers.Location.AbsoluteUri;
Assert.Contains("https://www.facebook.com/v3.3/dialog/oauth", location);
Assert.Contains("https://www.facebook.com/v4.0/dialog/oauth", location);
Assert.Contains("response_type=code", location);
Assert.Contains("client_id=", location);
Assert.Contains("redirect_uri=" + UrlEncoder.Default.Encode("http://example.com/signin-facebook"), location);
@ -291,7 +291,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
var transaction = await server.SendAsync("http://example.com/challenge");
Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode);
var location = transaction.Response.Headers.Location.AbsoluteUri;
Assert.Contains("https://www.facebook.com/v3.3/dialog/oauth", location);
Assert.Contains("https://www.facebook.com/v4.0/dialog/oauth", location);
Assert.Contains("response_type=code", location);
Assert.Contains("client_id=", location);
Assert.Contains("redirect_uri=", location);

View File

@ -76,9 +76,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.False(https);
}
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win8, WindowsVersions.Win81, WindowsVersions.Win2008R2, SkipReason = "UnixDomainSocketEndPoint is not supported on older versions of Windows")]
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/14382", Queues = "Windows.10.Amd64.Open")]
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_RS4)]
public void ParseAddressUnixPipe()
{
var listenOptions = AddressBinder.ParseAddress("http://unix:/tmp/kestrel-test.sock", out var https);

View File

@ -320,7 +320,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
// [InlineData("http2", HttpProtocols.Http2)] // Not supported due to missing ALPN support. https://github.com/dotnet/corefx/issues/33016
[InlineData("http1AndHttp2", HttpProtocols.Http1AndHttp2)] // Gracefully falls back to HTTP/1
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win10, WindowsVersions.Win8, WindowsVersions.Win81)]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win10, WindowsVersions.Win81)]
public void DefaultConfigSectionCanSetProtocols_MacAndWin7(string input, HttpProtocols expected)
=> DefaultConfigSectionCanSetProtocols(input, expected);
@ -329,7 +329,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
[InlineData("http2", HttpProtocols.Http2)]
[InlineData("http1AndHttp2", HttpProtocols.Http1AndHttp2)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7)]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win81)]
public void DefaultConfigSectionCanSetProtocols_NonMacAndWin7(string input, HttpProtocols expected)
=> DefaultConfigSectionCanSetProtocols(input, expected);
@ -389,7 +389,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
// [InlineData("http2", HttpProtocols.Http2)] // Not supported due to missing ALPN support. https://github.com/dotnet/corefx/issues/33016
[InlineData("http1AndHttp2", HttpProtocols.Http1AndHttp2)] // Gracefully falls back to HTTP/1
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win10, WindowsVersions.Win8, WindowsVersions.Win81)]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win10, WindowsVersions.Win81)]
public void EndpointConfigSectionCanSetProtocols_MacAndWin7(string input, HttpProtocols expected) =>
EndpointConfigSectionCanSetProtocols(input, expected);
@ -398,7 +398,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
[InlineData("http2", HttpProtocols.Http2)]
[InlineData("http1AndHttp2", HttpProtocols.Http1AndHttp2)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7)]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win81)]
public void EndpointConfigSectionCanSetProtocols_NonMacAndWin7(string input, HttpProtocols expected) =>
EndpointConfigSectionCanSetProtocols(input, expected);

View File

@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
[ConditionalFact]
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win10, WindowsVersions.Win8, WindowsVersions.Win81)]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win10, WindowsVersions.Win81)]
// Win7 SslStream is missing ALPN support.
public void TlsAndHttp2NotSupportedOnWin7()
{

View File

@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
[CollectDump]
[ConditionalFact]
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/9985", Queues = "Fedora.28.Amd64.Open")] // https://github.com/aspnet/AspNetCore/issues/9985
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/9985", Queues = "Fedora.28.Amd64.Open")]
[Flaky("https://github.com/aspnet/AspNetCore/issues/9985", FlakyOn.All)]
public async Task GracefulShutdownWaitsForRequestsToFinish()
{

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
#if LIBUV
[OSSkipCondition(OperatingSystems.Windows, SkipReason = "Libuv does not support unix domain sockets on Windows.")]
#else
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win8, WindowsVersions.Win81, WindowsVersions.Win2008R2, SkipReason = "UnixDomainSocketEndPoint is not supported on older versions of Windows")]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_RS4)]
#endif
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/14382", Queues = "Windows.10.Amd64.Open")]
[ConditionalFact]

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Xunit.Sdk;
namespace OpenQA.Selenium
@ -13,15 +14,31 @@ namespace OpenQA.Selenium
// case.
public class BrowserAssertFailedException : XunitException
{
public BrowserAssertFailedException(IReadOnlyList<LogEntry> logs, Exception innerException, string screenShotPath)
: base(BuildMessage(innerException, logs, screenShotPath), innerException)
public BrowserAssertFailedException(IReadOnlyList<LogEntry> logs, Exception innerException, string screenShotPath, string innerHTML)
: base(BuildMessage(innerException, logs, screenShotPath, innerHTML), innerException)
{
}
private static string BuildMessage(Exception innerException, IReadOnlyList<LogEntry> logs, string screenShotPath) =>
innerException.ToString() + Environment.NewLine +
(File.Exists(screenShotPath) ? $"Screen shot captured at '{screenShotPath}'" + Environment.NewLine : "") +
(logs.Count > 0 ? "Encountered browser logs" : "No browser logs found") + " while running the assertion." + Environment.NewLine +
string.Join(Environment.NewLine, logs);
private static string BuildMessage(Exception exception, IReadOnlyList<LogEntry> logs, string screenShotPath, string innerHTML)
{
var builder = new StringBuilder();
builder.AppendLine(exception.ToString());
if (File.Exists(screenShotPath))
{
builder.AppendLine($"Screen shot captured at '{screenShotPath}'");
}
if (logs.Count > 0)
{
builder.AppendLine("Encountered browser errors")
.AppendJoin(Environment.NewLine, logs);
}
builder.AppendLine("Page content:")
.AppendLine(innerHTML);
return builder.ToString();
}
}
}

Some files were not shown because too many files have changed in this diff Show More