dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.

Home Page:https://asp.net

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

minimal api-- FromQuery problem for complex object

infofromca opened this issue · comments

commented

Background and Motivation

when i use web api (mvc)

public class ShoppingCartController : Controller
{    
    [HttpGet]
    [Route("estimate/{sku}/{shoppingCartId?}")]
    public async Task<ActionResult<ShoppingCartLineViewModelVM>> EstimateProductAsync(
            string sku,
            string? shoppingCartId,
            [FromQuery] Address? shipping,
            [FromQuery] Address? billing
        )
        )

everything is working as expected.

but when i use minimal api, got exception

  public static IEndpointRouteBuilder AddEstimateProductAsyncEndpoint(this ```
IEndpointRouteBuilder builder)
    {
        builder.MapGet("api/ShoppingCart/EstimateProduct/{sku}/{shoppingCartId}", EstimateProductAsync)
            .AllowAnonymous()
            .DisableAntiforgery();
        return builder;
    }   
    private static async Task<IResult> EstimateProductAsync(
        string sku,
        string? shoppingCartId,
        [FromQuery] Address? shipping,
        [FromQuery] Address? billing     
        )
which indicated both  [FromQuery] Address? shipping,
    [FromQuery] Address? billing   are UNKNOWN AND SHOULD HAVE TryParce method

Proposed API

minimal api should be better than mvc api , at least the same

@infofromca please give a minimal repro sample.
Or at least show what exception you get, and how Address is defined. Otherwise this issue isn't actionable, we know too little of the problem.

commented

屏幕截图 2024-05-15 223935
屏幕截图 2024-05-15 224116

 public static IEndpointRouteBuilder AddEstimateProductAsyncEndpoint(this IEndpointRouteBuilder builder)
 {
     builder.MapGet("api/ShoppingCart/Address/{sku}/{shoppingCartId}", EstimateAsync)
         .AllowAnonymous()
         .DisableAntiforgery();

     return builder;
 }

 [Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "Api")]
 private static async Task<IResult> EstimateAsync(
     string sku,
     string? shoppingCartId,
     [FromQuery] Address? shipping,
     [FromQuery] Address? billing,
     IAuthorizationService authorizationService,
     HttpContext httpContext,
     IShoppingCartHelpers shoppingCartHelpers
     )
 {
public class Address
{
    private string DebuggerDisplay => $"{Name}, {StreetAddress1}, {City}";

    public string Name { get; set; }

    public string Department { get; set; }

    public string Company { get; set; }

    public string StreetAddress1 { get; set; }

    public string StreetAddress2 { get; set; }

    public string City { get; set; }

    public string Province { get; set; }

    public string PostalCode { get; set; }

    public string Region { get; set; }

    /// <summary>
    /// Gets a collection of name metadata. Some typical keys can be found in <see cref="CommonNameParts"/>.
    /// </summary>
    public IDictionary<string, string> NameParts { get; } = new Dictionary<string, string>();

    /// <summary>
    /// Gets a collection of other address metadata not related to names.
    /// </summary>
    public IDictionary<string, string> AdditionalFields { get; } = new Dictionary<string, string>();
}

please be notified that the exception did not happen at calling the API, it happened when run the site at the beginning (home page) , I do not think it matters with Address , it matters with any complex class with FromQuery, which do not have : shipping must have a valid TryParse method to support converting from a string , this is also shown on your document. but here Address is just for passing data. it is very very very strange for Address to implement TryParse method!!!
My guess why you need TryParse is because you use System.Text.Json internally. but could you implement a default stuff to deal with this TryParse in at least common situation? you can see the above Address is not very complex actually. Finally, Remember Address is just for data transfer on internet.

it is very very very strange for Address to implement TryParse method!!!

When you try to get Address from the query, that method is needed and totally makes sense.
Such an object like Address is typically passed in the body as JSON -- there no TryParse method is needed as the default JSON de-serialization kicks in.

Why do you need to have Address from the query?
Also keep in mind that the query may get very long, and may expose private data, which is better suited to the request body.

commented

When you try to get Address from the query, that method is needed and totally makes sense. Such an object like Address is typically passed in the body as JSON -- there no TryParse method is needed as the default JSON de-serialization kicks in.

why i do not need anything you said on mvc (web api), and it is concise:
public class ShoppingCartController : Controller
{
[HttpGet]
[Route("estimate/{sku}/{shoppingCartId?}")]
public async Task<ActionResult> EstimateProductAsync(
string sku,
string? shoppingCartId,
[FromQuery] Address? shipping,
[FromQuery] Address? billing
)
)
pay attention there is no TryParse method in Address. and it is the same logic

commented

Why do you need to have Address from the query? Also keep in mind that the query may get very long, and may expose >private data, which is better suited to the request body.

This is another topic, i think.
for keep in mind that the query may get very long, can you implement something like mvc first by default , then let dev to decide if they need tryParse to make it short

MVC has lots more built-in behaviour for binding that Minimal APIs intentionally tries to omit, as they often use reflection and aren't AoT friendly. 1-to-1 feature parity between the two frameworks isn't a goal.

commented

MVC has lots more built-in behaviour for binding that Minimal APIs intentionally tries to omit, as they often use reflection and aren't AoT friendly. 1-to-1 feature parity between the two frameworks isn't a goal.

so , why microsoft do not want to make progress, but regress.
and i guess here it is not reflection directly, it is STJ, even if reflection is a problem, you should think other way.....