Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for PageBinderFactory

[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.RazorPages.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Xunit;

Expand Down Expand Up @@ -420,14 +419,16 @@ public async Task PageConventions_CustomizedModelCanPostToHandlers()
var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(await getPage.Content.ReadAsStringAsync(), "");
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getPage);

var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel");
message.Content = new FormUrlEncodedContent(new Dictionary<string, string>
var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other tests using /CustomModelTypeModel aren't affected by the validation failure.

{
["__RequestVerificationToken"] = token,
["ConfirmPassword"] = "",
["Password"] = "",
["Email"] = ""
});
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["__RequestVerificationToken"] = token,
["ConfirmPassword"] = "",
["Password"] = "",
["Email"] = ""
})
};
message.Headers.TryAddWithoutValidation("Cookie", $"{cookie.Key}={cookie.Value}");

// Act
Expand All @@ -443,18 +444,20 @@ public async Task PageConventions_CustomizedModelCanPostToHandlers()
public async Task PageConventions_CustomizedModelCanWorkWithModelState()
{
// Arrange
var getPage = await Client.GetAsync("/CustomModelTypeModel");
var getPage = await Client.GetAsync("/CustomModelTypeModel?Attempts=0");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't strictly necessary because the OnGet(...) handler doesn't check ModelState.IsValid.

var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(await getPage.Content.ReadAsStringAsync(), "");
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getPage);

var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel");
message.Content = new FormUrlEncodedContent(new Dictionary<string, string>
var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel?Attempts=3")
{
["__RequestVerificationToken"] = token,
["Email"] = "javi@example.com",
["Password"] = "Password.12$",
["ConfirmPassword"] = "Password.12$",
});
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["__RequestVerificationToken"] = token,
["Email"] = "javi@example.com",
["Password"] = "Password.12$",
["ConfirmPassword"] = "Password.12$",
})
};
message.Headers.TryAddWithoutValidation("Cookie", $"{cookie.Key}={cookie.Value}");

// Act
Expand All @@ -465,6 +468,37 @@ public async Task PageConventions_CustomizedModelCanWorkWithModelState()
Assert.Equal("/", response.Headers.Location.ToString());
}

[Fact]
public async Task PageConventions_CustomizedModelCanWorkWithModelState_EnforcesBindRequired()
{
// Arrange
var getPage = await Client.GetAsync("/CustomModelTypeModel?Attempts=0");
var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(await getPage.Content.ReadAsStringAsync(), "");
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getPage);

var message = new HttpRequestMessage(HttpMethod.Post, "/CustomModelTypeModel")
{
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["__RequestVerificationToken"] = token,
["Email"] = "javi@example.com",
["Password"] = "Password.12$",
["ConfirmPassword"] = "Password.12$",
})
};
message.Headers.TryAddWithoutValidation("Cookie", $"{cookie.Key}={cookie.Value}");

// Act
var response = await Client.SendAsync(message);

// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var responseText = await response.Content.ReadAsStringAsync();
Assert.Contains(
"A value for the &#x27;Attempts&#x27; parameter or property was not provided.",
responseText);
}

[Fact]
public async Task ValidationAttributes_OnTopLevelProperties()
{
Expand Down Expand Up @@ -642,10 +676,12 @@ public async Task RoundTrippingFormFileInputWorks()

var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(response);

var content = new MultipartFormDataContent();
content.Add(new StringContent("property1-value"), property1);
content.Add(new StringContent("test-value1"), file1, "test1.txt");
content.Add(new StringContent("test-value2"), file3, "test2.txt");
var content = new MultipartFormDataContent
{
{ new StringContent("property1-value"), property1 },
{ new StringContent("test-value1"), file1, "test1.txt" },
{ new StringContent("test-value2"), file3, "test2.txt" }
};

var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +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.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Primitives;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
using Xunit;

namespace Microsoft.AspNetCore.Mvc.IntegrationTests
Expand Down Expand Up @@ -179,6 +181,74 @@ public async Task BindModelAsync_WithBindProperty_EnforcesBindRequired(int? inpu
}
}

[Theory]
[InlineData(null, false)]
[InlineData(123, true)]
public async Task BindModelAsync_WithBindPageProperty_EnforcesBindRequired(int? input, bool isValid)
{
// Arrange
var propertyInfo = typeof(TestPage).GetProperty(nameof(TestPage.BindRequiredProperty));
var propertyDescriptor = new PageBoundPropertyDescriptor
{
BindingInfo = BindingInfo.GetBindingInfo(new[]
{
new FromQueryAttribute { Name = propertyInfo.Name },
}),
Name = propertyInfo.Name,
ParameterType = propertyInfo.PropertyType,
Property = propertyInfo,
};

var typeInfo = typeof(TestPage).GetTypeInfo();
var actionDescriptor = new CompiledPageActionDescriptor
{
BoundProperties = new[] { propertyDescriptor },
HandlerTypeInfo = typeInfo,
ModelTypeInfo = typeInfo,
PageTypeInfo = typeInfo,
};

var testContext = ModelBindingTestHelper.GetTestContext(request =>
{
request.Method = "POST";
if (input.HasValue)
{
request.QueryString = new QueryString($"?{propertyDescriptor.Name}={input.Value}");
}
});

var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(modelMetadataProvider);
var modelBinderFactory = ModelBindingTestHelper.GetModelBinderFactory(modelMetadataProvider);
var modelMetadata = modelMetadataProvider
.GetMetadataForProperty(typeof(TestPage), propertyDescriptor.Name);

var pageBinder = PageBinderFactory.CreatePropertyBinder(
parameterBinder,
modelMetadataProvider,
modelBinderFactory,
actionDescriptor);
var pageContext = new PageContext
{
ActionDescriptor = actionDescriptor,
HttpContext = testContext.HttpContext,
RouteData = testContext.RouteData,
ValueProviderFactories = testContext.ValueProviderFactories,
};

var page = new TestPage();

// Act
await pageBinder(pageContext, page);

// Assert
Assert.Equal(isValid, pageContext.ModelState.IsValid);
if (isValid)
{
Assert.Equal(input.Value, page.BindRequiredProperty);
}
}

[Theory]
[InlineData("RequiredAndStringLengthProp", null, false)]
[InlineData("RequiredAndStringLengthProp", "", false)]
Expand Down Expand Up @@ -231,12 +301,18 @@ public async Task BindModelAsync_WithBindProperty_EnforcesDataAnnotationsAttribu
}
}

class TestController
private class TestController
{
[BindNever] public string BindNeverProp { get; set; }
[BindRequired] public int BindRequiredProp { get; set; }
[Required, StringLength(3)] public string RequiredAndStringLengthProp { get; set; }
[DisplayName("My Display Name"), StringLength(3)] public string DisplayNameStringLengthProp { get; set; }
}

private class TestPage : PageModel
{
[BindRequired]
public int BindRequiredProperty { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

Expand All @@ -13,6 +14,10 @@ public class CustomModelTypeModel : PageModel

public string ReturnUrl { get; set; }

[BindRequired]
[FromQuery(Name = nameof(Attempts))]
public int Attempts { get; set; }

public class InputModel
{
[Required]
Expand Down Expand Up @@ -69,10 +74,13 @@ public override IActionResult OnPostAsync(string returnUrl = null)
{
if (!ModelState.IsValid)
{
Attempts++;
RouteData.Values.Add(nameof(Attempts), Attempts);

return Page();
}

return Redirect("~/");
}
}
}
}