Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discussion] [Breaking change]: API Controllers Actions try to infer parameters from DI #40071

Closed
1 of 2 tasks
brunolins16 opened this issue Feb 8, 2022 · 3 comments
Closed
1 of 2 tasks
Labels
area-web-frameworks *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels
Milestone

Comments

@brunolins16
Copy link
Member

brunolins16 commented Feb 8, 2022

Description

The mechanism to infer binding source of API Controller action's parameters now mark parameters to be bound from the Dependency Injection container when the type is registered in the container.

In rare cases this can break applications that have a type in DI that is also accepted in API Controller actions methods.

Version

7.0.0-preview2

Previous behavior

Before if you want to bind a type registered in your Dependency Injection container, it must be explicitly decorated using an attribute that implements IFromServiceMetadata (eg.: FromServicesAttribute)

Services.AddScoped<SomeCustomType>();

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    public ActionResult Get([FromServices]SomeCustomType service) => Ok();
}

If the attribute is not specified, the parameter is resolved from the request Body sent by the client.

Services.AddScoped<SomeCustomType>();

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    // Bind from the request body
    [HttpPost]
    public ActionResult Post(SomeCustomType service) => Ok();
}

New behavior

Now types in DI will be checked at app startup using IServiceProviderIsService to determine if an argument in an API controller action will come from DI or from the other sources.

In the below example SomeCustomType (assuming you're using the default DI container) will come from the DI container.

Services.AddScoped<SomeCustomType>();

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    // Binding from the services
    [HttpPost]
    public ActionResult Post(SomeCustomType service) => Ok();
}

The new mechanism to infer binding source of API Controller action's parameters will follow the rule bellow:

  1. A previously specified BindingInfo.BindingSource is never overwritten.
  2. A complex type parameter, registered in the DI container, is assigned BindingSource.Services.
  3. A complex type parameter, not registered in the DI container, is assigned BindingSource.Body.
  4. Parameter with a name that appears as a route value in ANY route template is assigned BindingSource.Path.
  5. All other parameters are BindingSource.Query.

Type of breaking change

  • Binary incompatible: Existing binaries may encounter a breaking change in behavior, such as failure to load/execute or different run-time behavior.
  • Source incompatible: Source code may encounter a breaking change in behavior when targeting the new runtime/component/SDK, such as compile errors or different run-time behavior.

Reason for change

We believe the likelihood of breaking apps to be very low as it's not a common scenario to have a type in DI and as an argument in your API controller action at the same time. Also, this same behavior is currently supported by Minimal Actions.

Recommended action

If you are broken by this change you can disable the feature by setting DisableImplicitFromServicesParameters to true.

services.Configure<ApiBehaviorOptions>(options => {
     options.DisableImplicitFromServicesParameters = true;            
});

Also, you could continue to have your action's parameters, with the new feature enabled or not, binding from your DI container using an attribute that implements IFromServiceMetadata (eg.: FromServicesAttribute).

Services.AddScoped<SomeCustomType>();

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
     // Binding from the DI container
    [HttpPost]
    public ActionResult Post([FromServices]SomeCustomType service) => Ok();
}

Affected APIs

API Controller actions.

Announcement

aspnet/Announcements#480

@brunolins16 brunolins16 added the area-web-frameworks *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels label Feb 8, 2022
@brunolins16 brunolins16 added this to the Discussions milestone Feb 8, 2022
@tillig
Copy link
Contributor

tillig commented Feb 8, 2022

Request for clarification: IServiceProviderIsService may not always be 100% accurate given the dynamic ability for things to be registered via assembly scanning, during request lifetimes, etc. Can you still force a parameter to be resolved from DI using the [FromServices] attribute?

@brunolins16
Copy link
Member Author

@tillig, yes. all the attributes still working and take the precedent over all infer logic.

The new mechanism to infer binding source of API Controller action's parameters will follow the rule bellow:

  1. A previously specified BindingInfo.BindingSource is never overwritten.
  2. A complex type parameter, registered in the DI container, is assigned BindingSource.Services.
  3. A complex type parameter, not registered in the DI container, is assigned BindingSource.Body.
  4. Parameter with a name that appears as a route value in ANY route template is assigned BindingSource.Path.
  5. All other parameters are BindingSource.Query.

@ghost
Copy link

ghost commented Apr 9, 2022

Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.

This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!

@ghost ghost closed this as completed Apr 9, 2022
@ghost ghost locked as resolved and limited conversation to collaborators May 9, 2022
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-web-frameworks *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels
Projects
None yet
Development

No branches or pull requests

2 participants