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

More Information for WebAPI MediaTypeFormatter #611

Closed
ErikXu opened this issue Jan 9, 2015 · 2 comments
Closed

More Information for WebAPI MediaTypeFormatter #611

ErikXu opened this issue Jan 9, 2015 · 2 comments
Assignees
Labels

Comments

@ErikXu
Copy link

ErikXu commented Jan 9, 2015

I want to know more about the registration for MediaTypeFormatter while the introduction on http://autofac.readthedocs.org/en/latest/integration/webapi.html#supported-services is too simple. Could you please help to solve the questions for me?
#1 How to add a custom media formatter to the HttpConfiguration with dependence?
#2 How to replace a existed media formatter in the HttpConfiguration with dependence?

@tillig
Copy link
Member

tillig commented Jan 17, 2015

Web API doesn't pass the set of MediaTypeFormatter instances attached to HttpConfiguration.Formatters through dependency injection - it's a static set of entities.

If you want to integrate DI with your root set of media type formatters, it's going to be something custom you'll need to build. There is no guidance we have available.

One idea (not guidance, just an idea) is to build the container and then resolve the set of formatters.

var builder = new ContainerBuilder();
builder.RegisterType<MyFirstFormatter>().As<MediaTypeFormatter>();
builder.RegisterType<MySecondFormatter>().As<MediaTypeFormatter>();
builder.RegisterType<MyDependency>().As<IDependency>();
var container = builder.Build();

foreach(var formatter in container.Resolve<IEnumerable<MediaTypeFormatter>())
{
  config.MediaTypeFormatters.Add(formatter);
}

Note these would be singletons and would be resolved from the root container - you wouldn't get per-request dependencies or anything like that. For the global level, I don't see any easy way around that.

Now, I do see that the MediaTypeFormatter class has a method GetPerRequestFormatterInstance which, by default, just returns the current instance.

You might be able to do something with that. You get the HttpRequestMessage in there, so you could do something like this:

public class MyFormatter : MediaTypeFormatter
{
  public MyFormatter()
  {
    // Default behavior. Probably(?) won't be used.
  }

  public MyFormatter(IDependency dependency)
  {
    // The "real" constructor.
  }

  public override MediaTypeFormatter GetPerRequestFormatterInstance(
    Type type,
    HttpRequestMessage request,
    MediaTypeHeaderValue mediaType)
  {
    // Run a resolve to get the dependencies injected.
    return request.GetDependencyScope(typeof(MyFormatter)) as MyFormatter;
  }
}

You'd still have to register a "placeholder" for the media type formatter with the static collection, but hypothetically this would add DI to it.

// Use the "default" constructor just to get the formatter
// into the collection.
config.MediaTypeFormatters.Add(new MyFormatter());

Now, as mentioned in the docs, you can use the [AutofacControllerConfiguration] attribute to get per-controller media type formatters. Here's one of our unit tests showing that in action.

[Test]
public void FormattersCanBeResolvedPerControllerType()
{
    var builder = new ContainerBuilder();
    var formatter1 = new Mock<MediaTypeFormatter>().Object;
    var formatter2 = new Mock<MediaTypeFormatter>().Object;
    builder.RegisterInstance(formatter1).InstancePerApiControllerType(typeof(TestController));
    builder.RegisterInstance(formatter2).InstancePerApiControllerType(typeof(TestController));
    var container = builder.Build();
    var configuration = new HttpConfiguration {DependencyResolver = new AutofacWebApiDependencyResolver(container)};
    var settings = new HttpControllerSettings(configuration);
    var descriptor = new HttpControllerDescriptor(configuration, "TestController", typeof(TestController));
    var attribute = new AutofacControllerConfigurationAttribute();

    attribute.Initialize(settings, descriptor);

    Assert.That(settings.Formatters.Count, Is.EqualTo(6));
    Assert.That(settings.Formatters.Contains(formatter1), Is.True);
    Assert.That(settings.Formatters.Contains(formatter2), Is.True);
}

That works on a per-controller basis, but that's the only actual integration we have for DI in media type formatters.

Hopefully between the various ideas above it should unblock you. If not, it might be a good question to open on StackOverflow to see if the larger community has ideas.

@tillig tillig closed this as completed Jan 17, 2015
@ErikXu
Copy link
Author

ErikXu commented Jan 26, 2015

The second one is a good solution for me. Thanks for your reply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants