Skip to content

RequestDelegateFactory should produce clear errors for invalid Delegates #30322

Closed
@davidfowl

Description

@davidfowl

Is your feature request related to a problem? Please describe.

We should make sure its clear what is supported in MapAction instead of it failing with hard to grok errors. For example, #31658 (comment)

Describe the solution you'd like

I think we should have both runtime and compile time validation (an analyzer where possible).

Scenarios for MapActions

1. Forgetting to register a service with the DI container generates an incorrect error as the parameter is viewed as second [FromBody] attribute. Although from the user standpoint the second [FromBody] attribute is not explicit - #35086

app.MapPost("/createUser", ([FromBody] UserDTO userDTO, UserRepository userRepo) => {
    return $"user created  - {userDTO.LastName} - {userRepo.CreateUser(userDTO).LastName}";
 });

The user's intention was to inject the UserRepository as service to use.

Error message:

System.InvalidOperationException: 'Action cannot have more than one FromBody attribute.'

2. Allowing query strings on Post is not semantically correct and might be viewed as a security issue. The user would have expected the [FromQuery] attribute to be ignored/should not bind or should throw an exception at the startup time. Nothing to do.

 app.MapPost("/createUser", ([FromQuery] string userName, [FromBody] User user,) => $"user created- {userName} - {user.LastName} ");

3. FromBody attribute on MapGet does not throw an exception. (User could have copied the method and forget to remove the frombody). Postman allows you to add a body to a get method. However, without a body added to the request, we get 400 which is the correct behavior. If the body is present, binding happens. - Nothing to do - this is by design

builder.Services.AddSingleton<IUserRepository, UserRepository>()

app.MapGet("/findUser/{id}", (int id, [FromBody] UserDTO userDTO, IUserRepository userRepo) => {
    return $"user found - {userDTO.LastName} - {userRepo.FindUserById(id).LastName}";
})

4. In MapGet an implicit injection of any service returns a 400 if the user forgot to register the service in the DI container. However, an explicit use of [FromServices] returns the correct error. This is inconsistent - #35086

  app.MapGet("/findUser/{id}", (int id, UserRepository userRepo) => {
    return $"user found - {id} - {userRepo.FindUserById(id).LastName}";
  });

Error for explicit use of [FromServices]

  app.MapGet("/findUser/{id}", (int id, [FromServices] IUserRepository userRepo) => {
    return $"user found - {id} - {userRepo.FindUserById(id).LastName}";
  });
  System.InvalidOperationException: No service for type 'IUserRepository' has been registered.
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, 
  Type serviceType)
....

5. Improve the error message when routes parameters do match or just ignore casing(lower/upper) - This is a common mistake that users may make - #35087

   app.MapGet("/user/{FirstName}/{lastName}", ([FromRoute()]string? firstName, [FromRoute()]string lastName) => 
    UserService.GetUser(firstName, lastName, "aa"));

Error message (Notice FirstName != firstName):

System.InvalidOperationException: 'firstName is not a route paramter.'

6. What should we do when someone uses attributes that should not be allowed on certain methods ? see example below: -#35088

  app.MapGet("/user", ([Bind("FirstName,LastName")] User user) => $"username {user.FirstName}");

7. Adding an optional/nullability (?) in the route pattern, messes up the Swagger UI and the json file still recognizes as required field. - #35081

   app.MapGet("/user/{id?}", (int? id) => $"userId {id}");

Notice the missing closing bracket
image

Metadata

Metadata

Assignees

Labels

Priority:1Work that is critical for the release, but we could probably ship withoutarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-minimal-actionsController-like actions for endpoint routingold-area-web-frameworks-do-not-use*DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions