覆盖ApiController中void方法的HttpResponseMessage的创建

时间:2015-02-18 16:55:49

标签: c# .net asp.net-web-api

我正在构建一个扩展ApiController的课程,而这个课程将由我的客户进行扩展。

在我的控制器类中,我需要更改操作的返回值的序列化方式。

例如:默认情况下,执行以下操作:

public Person Get() { return new Person(); }

将生成HTTP响应,例如:

HTTP/1.1 200 OK

{"name":null}

如果我想拦截这个Person对象,我可以从MediaTypeFormatter扩展,并将它附加到控制器的Configuration.Formatters集合,并按照我想要的方式序列化对象。< / p>

public abstract class MyController : ApiController
{
    protected override void Initialize(HttpControllerContext controllerContext)
    {
        base.Initialize(controllerContext);

        Configuration.Formatters.OfType<JsonMediaTypeFormatter>().ToList()
            .ForEach(f => Configuration.Formatters.Remove(f));

        Configuration.Formatters.Insert(0, new MyPersonFormatter());
    }

现在,该操作将格式化为:

HTTP/1.1 200 OK

{"my custom wrapper":{"name":null}}

但是,如果我想拦截无效行动怎么办?

默认情况下,void操作将转换为具有空体的204 No Content响应。我如何将数据插入到响应的正文中?

注意:如果可以在控制器内完成,我希望我的抽象控制器能够自行完成所有管道工作,而不是在客户端强制执行任何操作。

1 个答案:

答案 0 :(得分:2)

在内部,在默认的HttpActionDescriptor中,框架将ResultConvertervoid方法设置为自己的VoidResultConverter,它返回带有空内容的HTTP 204。另外,通过明确地将其称为VoidResultConverter,您甚至不能强迫它使用Reflection替换其实现。

HttpActionDescriptor.cs中的第34行:

private static readonly VoidResultConverter _voidResultConverter = new VoidResultConverter();

但是,您可以使用自定义ActionDescriptor替换voidActionFilter方法(全局,基本控制器中,或使用属性在某些操作上明确定义) :

public class VoidActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var retType = actionContext.ActionDescriptor.ReturnType;

        if (retType == typeof(void) || retType == null)
        {
            actionContext.ActionDescriptor = new VoidActionDescriptor(actionContext.ActionDescriptor);
        }

        base.OnActionExecuting(actionContext);
    }
}

这里是VoidActionDescriptor,它实际上是常规ReflectedHttpActionDescriptor的包装:

public class VoidActionDescriptor : HttpActionDescriptor
{
    private readonly HttpActionDescriptor _currentDescriptor;

    public VoidActionDescriptor(HttpActionDescriptor currentDescriptor)
    {
        if (currentDescriptor == null)
            throw new ArgumentNullException("currentDescriptor");

        this._currentDescriptor = currentDescriptor;
    }

    // this is what we're here for
    public override IActionResultConverter ResultConverter
    {
        get { return new MyVoidResultConverter(); }
    }

    // wrapper methods from now on
    public override Collection<HttpParameterDescriptor> GetParameters()
    {
        return this._currentDescriptor.GetParameters();
    }

    public override Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken)
    {
        return this._currentDescriptor.ExecuteAsync(controllerContext, arguments, cancellationToken);
    }

    public override string ActionName
    {
        get { return this._currentDescriptor.ActionName; }
    }

    public override Type ReturnType
    {
        get { return this._currentDescriptor.ReturnType; }
    }
}

它唯一有趣的事情是将自定义MyVoidResultConverter返回到其ResultConverter属性中。

现在最后一块是实际的IActionResultConverter

public class MyVoidResultConverter : IActionResultConverter
{
    public HttpResponseMessage Convert(HttpControllerContext controllerContext, object actionResult)
    {
        var res = controllerContext.Request.CreateResponse(HttpStatusCode.OK);
        res.Content = new StringContent("void response");

        return res;
    }
}