It would have been easier had IActionResultConverter been replaceable as a Service....but currently its not. Following is a way I have tried and this seems to work and its similar to what you mentioned above. I use a wrapper ReflectedHttpActionDescriptor to which I supply the default implementation and I just provide my custom implementation for ResultConverter only and for the remaining delegate it to the original one.
public class CustomActionSelector : ApiControllerActionSelector
{
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
return new WrapperReflectedHttpActionDescriptor(base.SelectAction(controllerContext));
}
public override ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor)
{
//Wrap the original action descriptor into Wrapper action descriptor
return base.GetActionMapping(controllerDescriptor)
.SelectMany(g => g, (g, v) => new KeyValuePair<string, HttpActionDescriptor>(g.Key, new WrapperReflectedHttpActionDescriptor(v)))
.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
}
}
public class WrapperReflectedHttpActionDescriptor : ReflectedHttpActionDescriptor
{
private HttpActionDescriptor _original;
public WrapperReflectedHttpActionDescriptor(HttpActionDescriptor original)
{
this.Configuration = original.Configuration;
this.ControllerDescriptor = original.ControllerDescriptor;
this._original = original;
}
public override HttpActionBinding ActionBinding
{
get
{
return this._original.ActionBinding;
}
set
{
this._original.ActionBinding = value;
}
}
public override string ActionName
{
get
{
return this._original.ActionName;
}
}
public override System.Threading.Tasks.Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, System.Threading.CancellationToken cancellationToken)
{
return this._original.ExecuteAsync(controllerContext, arguments, cancellationToken);
}
public override System.Collections.ObjectModel.Collection<T> GetCustomAttributes<T>()
{
return this._original.GetCustomAttributes<T>();
}
public override System.Collections.ObjectModel.Collection<System.Web.Http.Filters.FilterInfo> GetFilterPipeline()
{
return this._original.GetFilterPipeline();
}
public override System.Collections.ObjectModel.Collection<System.Web.Http.Filters.IFilter> GetFilters()
{
return this._original.GetFilters();
}
public override System.Collections.ObjectModel.Collection<HttpParameterDescriptor> GetParameters()
{
return this._original.GetParameters();
}
public override System.Collections.Concurrent.ConcurrentDictionary<object, object> Properties
{
get
{
return this._original.Properties;
}
}
public override IActionResultConverter ResultConverter
{
get
{
//provide custom one
return new CustomActionResultConverter();
}
}
public override Type ReturnType
{
get
{
return this._original.ReturnType;
}
}
public override System.Collections.ObjectModel.Collection<HttpMethod> SupportedHttpMethods
{
get
{
return this._original.SupportedHttpMethods;
}
}
}
public class CustomActionResultConverter : IActionResultConverter
{
public HttpResponseMessage Convert(HttpControllerContext controllerContext, object actionResult)
{
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new StringContent("Hello");
return response;
}
}