dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.22k stars 9.95k forks source link

DefaultModelBinder doesn't handle Uri property type correctly #48009

Open fknebels opened 1 year ago

fknebels commented 1 year ago

Is there an existing issue for this?

Describe the bug

You are able to pass non Uri strings in Uri property of webapi models.

so I can pass the word "string" in for a Uri property and the default modelbinder behavior will accept this value even though it does not conform to the Uri structure. if you look at the bound property in the controller action, Every property says "the property threw an exception of type 'System.InvalidOperationException'"

However the modelstate for the controller action show IsValid true.

Expected Behavior

The ModelBinder should fail to bind the object and the modelstate should be set correctly that the Uri could not be parsed.

Steps To Reproduce

Github repo. run the project run the post in swagger taking the default value "string" Post action should return a 201 even though the property is not of type Uri. Should have returned a 400 with a validation message that the property could not be parsed.

Exceptions (if any)

No response

.NET Version

7.0.202

Anything else?

.NET SDK: Version: 7.0.202 Commit: 6c74320bc3

Runtime Environment: OS Name: Windows OS Version: 10.0.19044 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\7.0.202\

Host: Version: 7.0.4 Architecture: x64 Commit: 0a396acafe

.NET SDKs installed: 2.2.207 [C:\Program Files\dotnet\sdk] 6.0.407 [C:\Program Files\dotnet\sdk] 7.0.202 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.26 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.26 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found: x86 [C:\Program Files (x86)\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables: Not set

global.json file: Not found

ghost commented 1 year ago

Thank you for filing this issue. In order for us to investigate this issue, please provide a minimalistic repro project that illustrates the problem.

fknebels commented 1 year ago

I thought I edited the first comment with the repo.

https://github.com/fknebels/UrlPropertyBinding

fknebels commented 1 year ago

Yup. That was my workaround.

public class UriAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        if (value == null)
        {
            return ValidationResult.Success;
        }

        if (value is not Uri valueString)
        {
            return new ValidationResult($"{validationContext.MemberName} could not be parsed as a Uri");
        }

        var isParsed = Uri.TryCreate(valueString.OriginalString, UriKind.Absolute, out _);

        return isParsed
            ? ValidationResult.Success
            : new ValidationResult($"{validationContext.MemberName} could not be parsed as a Uri");
    }
}