Ask Learn
Preview
Please sign in to use this experience.
Sign inThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
We’ve just added support for WebAPI to provide per-controller-type configuration. WebAPI has a HttpConfiguration object that provides configuration such as:
However, a specific controller may need its own specific services. And so we’ve added per-controller-type configuration. In essence, a controller type can have its own “shadow copy” of the global config object, and then override specific settings. This is automatically applied to all controller instances of the given controller-type. (This supersedes the HttpControllerConfiguration attribute that we had in Beta)
Some of the scenarios we wanted to enable here:
In all these cases, the controller is coupled to a specific service for basic correct operation, and these services really are private implementation of the controller that shouldn’t conflict with settings from other controllers. Per-controller config allows multiple controllers to override their own services and coexist peacefully in an app together.
How to setup per-controller config?
We’ve introduced the IControllerConfiguration interface:
public interface IControllerConfiguration
{
void Initialize(HttpControllerSettings controllerSettings,
HttpControllerDescriptor controllerDescriptor);
}
WebAPI will look for attributes on the controller that implement that interface, and then invoke them when initializing the controller-type. This follows the same inheritance order as constructors, so attributes on the base type will be invoked first.
The controllerSettings object specifies what things on the configuration can be overriden for a controller. This provides static knowledge of what things on a configuration can and can’t be specified for a controller. Obviously, things like message handlers and routes can’t be specified for a per-controller basis.
public sealed class HttpControllerSettings
{
public MediaTypeFormatterCollection Formatters { get; }
public ParameterBindingRulesCollection ParameterBindingRules { get; }
public ServicesContainer Services { get; }
}
So an initialization function can change the services, formatters, or binding rules. Then WebAPI will create a new shadow HttpConfiguration object and apply those changes. Things that are not changes will still fall through to the global configuration.
Example
Here’s an example. Suppose we have our own controller type, and we want it to only use a specific formatter and IActionValueBinder.
First, we add a config attribute:
[AwesomeConfig]
public class AwesomeController : ApiController
{
[HttpGet]
public string Action(string s)
{
return "abc";
}
}
That attribute implementss the IControllerConfiguration:
class AwesomeConfigAttribute : Attribute, IControllerConfiguration
{
public void Initialize(HttpControllerSettings controllerSettings,
HttpControllerDescriptor controllerDescriptor)
{
controllerSettings.Services.Replace(typeof(IActionValueBinder), new AwesomeActionValueBinder());
controllerSettings.Formatters.Clear();
controllerSettings.Formatters.Add(new AwesomeCustomFormatter());
}
}
This will clear all the default formatters and add our own AwesomeCustomFormatter. It will also the IActionValueBinder to our own AwesomeActionValueBinder. It also will not affect any other controllers in the system.
Setting a service on the controller here has higher precedence than setting services in the dependency resolver or in the global configuration.
The initialization function can also inspect incoming configuration and modify it. For example, it can append a formatter or binding rule to an existing list.
What happens under the hood?
This initialization function is invoked when WebAPI is first creating the HttpControllerDescriptor for this controller type. It’s only invoked once per controller type. WebAPI will then apply the controllerSettings and create a new HttpConfiguration object. There are some optimizations in place to make this efficient:
Then the resulting configuration is used for future instances of controller. Calling code still just gets a HttpConfiguration instance and doesn’t need to care whether that instance was the global configuration or a per-controller configuration. So when the controller asks for formatters or an IActionValueBinder here, it will automatically pull from the controller’s config instead of the global one.
Please sign in to use this experience.
Sign in