C# 使用IHttpActionResult时如何设置自定义标题?

C# 使用IHttpActionResult时如何设置自定义标题?,c#,asp.net,asp.net-web-api,C#,Asp.net,Asp.net Web Api,在ASP.NET Web API 2中,IHttpActionResult在简化控制器代码方面提供了很多价值,我不愿意停止使用它,但我遇到了一个问题 我需要在传出响应上设置ETag,但找不到任何允许我访问响应头的属性。目前,我正在使用ApiController中的Ok(T content)helper方法,该方法返回一个对象。这似乎没有任何公开的内容可以让我修改标题 使用stockIHttpActionResult类型时,我是否遗漏了什么,或者真的没有办法做到这一点?我考虑了一个消息处理程序,但

在ASP.NET Web API 2中,
IHttpActionResult
在简化控制器代码方面提供了很多价值,我不愿意停止使用它,但我遇到了一个问题

我需要在传出响应上设置ETag,但找不到任何允许我访问响应头的属性。目前,我正在使用
ApiController
中的
Ok(T content)
helper方法,该方法返回一个对象。这似乎没有任何公开的内容可以让我修改标题

使用stock
IHttpActionResult
类型时,我是否遗漏了什么,或者真的没有办法做到这一点?我考虑了一个消息处理程序,但接下来我必须弄清楚如何将ETag从操作中传递出去(ETag是为不同的操作生成的,因此不需要为所有操作生成通用处理程序)


我希望避免使用原始的HttpResponseMessage,但目前看来这很困难。

对于您的场景,您需要创建一个自定义的
ihttpackationresult
。下面是一个示例,我从运行
内容协商时的
OkNegotiatedContentResult
派生,并设置
Ok
状态代码

public class CustomOkResult<T> : OkNegotiatedContentResult<T>
{
    public CustomOkResult(T content, ApiController controller)
        : base(content, controller) { }

    public CustomOkResult(T content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters) 
        : base(content, contentNegotiator, request, formatters) { }

    public string ETagValue { get; set; }

    public override async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await base.ExecuteAsync(cancellationToken);

        response.Headers.ETag = new EntityTagHeaderValue(this.ETagValue);

        return response;
    }        
}
公共类CustomOkResult:OkNegotiatedContentResult
{
公共CustomOkResult(T内容,ApiController控制器)
:base(内容、控制器){}
公共CustomOkResult(T内容、IContentNegotiator内容谈判者、HttpRequestMessage请求、IEnumerable格式化程序)
:base(内容、内容协商器、请求、格式化程序){}
公共字符串ETagValue{get;set;}
公共覆盖异步任务ExecuteAsync(CancellationToken CancellationToken)
{
HttpResponseMessage response=await base.ExecuteAsync(cancellationToken);
response.Headers.ETag=新的EntityTagHeaderValue(this.ETagValue);
返回响应;
}        
}
控制器

public class ValuesController : ApiController
{
    public IHttpActionResult Get()
    {
        return new CustomOkResult<string>(content: "Hello World!", controller: this)
            {
                    ETagValue = "You ETag value"
            };
    }
}
公共类值控制器:ApiController
{
public IHttpActionResult Get()
{
返回新的CustomOkResult(内容:“Hello World!”,控制器:this)
{
ETagValue=“您可以使用ETag值”
};
}
}
请注意,您还可以从
NegotiatedContentResult
派生,在这种情况下,您需要自己提供状态代码。希望这有帮助


您可以找到的源代码,正如您所想象的那样,它们实际上很简单。

您可以创建一个
HttpResponseMessage
,根据需要添加标题,然后从中创建
ResponseMessageResult

HttpResponseMessage response =new HttpResponseMessage(HttpStatusCode.OK);
response.Headers.Add("MyHeader", "MyHeaderValue");
return ResponseMessage(response);

这可以通过ActionFilterAttribute实现,ActionFilterAttribute将在控制器功能完成后但在响应停止前检查响应,然后您可以在控制器方法上设置属性以添加此信息,下面是我的实现:

public class EnableETag : ActionFilterAttribute
{

    /// <summary>
    /// NOTE: a real production situation, especially when it involves a web garden
    ///       or a web farm deployment, the tags must be retrieved from the database or some other place common to all servers.
    /// </summary>
    private static ConcurrentDictionary<string, EntityTagHeaderValue> etags = new ConcurrentDictionary<string, EntityTagHeaderValue>();

    public override void OnActionExecuting(HttpActionContext context)
    {
        var request = context.Request;
        if (request.Method == HttpMethod.Get)
        {
            var key = GetKey(request);
            ICollection<EntityTagHeaderValue> etagsFromClient = request.Headers.IfNoneMatch;
            if (etagsFromClient.Count > 0)
            {
                EntityTagHeaderValue etag = null;
                if (etags.TryGetValue(key, out etag) && etagsFromClient.Any(t => t.Tag == etag.Tag))
                {
                    context.Response = new HttpResponseMessage(HttpStatusCode.NotModified);
                    SetCacheControl(context.Response);
                }
            }
        }
    }
    public override void OnActionExecuted(HttpActionExecutedContext context)
    {
        var request = context.Request;
        var key = GetKey(request);
        EntityTagHeaderValue etag;
        if (!etags.TryGetValue(key, out etag) || request.Method == HttpMethod.Put ||
        request.Method == HttpMethod.Post)
        {
            etag = new EntityTagHeaderValue("\"" + Guid.NewGuid().ToString() + "\"");
            etags.AddOrUpdate(key, etag, (k, val) => etag);
        }
        context.Response.Headers.ETag = etag;
        SetCacheControl(context.Response);
    }
    private string GetKey(HttpRequestMessage request)
    {
        return request.RequestUri.ToString();
    }

    /// <summary>
    /// Defines the time period to hold item in cache (currently 10 seconds)
    /// </summary>
    /// <param name="response"></param>
    private void SetCacheControl(HttpResponseMessage response)
    {
        response.Headers.CacheControl = new CacheControlHeaderValue()
        {
            MaxAge = TimeSpan.FromSeconds(10),
            MustRevalidate = true,
            Private = true
        };
    }
}
公共类启用tag:ActionFilterAttribute
{
/// 
///注:实际生产情况,尤其是涉及网络花园时
///对于web场部署,必须从数据库或所有服务器共用的其他位置检索标记。
/// 
私有静态ConcurrentDictionary etag=新ConcurrentDictionary();
公共重写无效OnActionExecuting(HttpActionContext上下文)
{
var-request=context.request;
if(request.Method==HttpMethod.Get)
{
var key=GetKey(请求);
ICollection etagsFromClient=request.Headers.IfNoneMatch;
如果(etagsFromClient.Count>0)
{
EntityTagHeaderValue etag=null;
if(etags.TryGetValue(key,out etag)和&etagsFromClient.Any(t=>t.Tag==etag.Tag))
{
context.Response=新的HttpResponseMessage(HttpStatusCode.NotModified);
SetCacheControl(context.Response);
}
}
}
}
公共覆盖无效OnActionExecuted(HttpActionExecutedContext)
{
var-request=context.request;
var key=GetKey(请求);
EntityTagHeaderValue etag;
如果(!etags.TryGetValue(key,out etag)| | request.Method==HttpMethod.Put||
request.Method==HttpMethod.Post)
{
etag=newEntityTagHeaderValue(“\”+Guid.NewGuid().ToString()+“\”);
添加或更新(键,etag,(k,val)=>etag);
}
context.Response.Headers.ETag=ETag;
SetCacheControl(context.Response);
}
私有字符串GetKey(HttpRequestMessage请求)
{
return request.RequestUri.ToString();
}
/// 
///定义在缓存中保留项的时间段(当前为10秒)
/// 
/// 
私有void SetCacheControl(HttpResponseMessage响应)
{
response.Headers.CacheControl=新的CacheControlHeaderValue()
{
最大年龄=从秒(10)开始的时间跨度,
MustRevalidate=true,
私有=真
};
}
}

}

我在我的通用Web API 2库代码中使用了一个解决方案,它可以轻松支持设置任何标题或
ExecuteAsync
中提供的
HttpResponseMessage
上的任何其他属性,而无需绑定到任何特定的派生
NegotiatedContentResult
实现:

public class FlexibleNegotiatedContentResult<T> : NegotiatedContentResult<T>
{
    private readonly Action<HttpResponseMessage> _responseMessageDelegate;

    public FlexibleNegotiatedContentResult(HttpStatusCode statusCode, T content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
        : base(statusCode, content, contentNegotiator, request, formatters)
    {
    }

    public FlexibleNegotiatedContentResult(HttpStatusCode statusCode, T content, ApiController controller, Action<HttpResponseMessage> responseMessageDelegate = null)
        : base(statusCode, content, controller)
    {
        _responseMessageDelegate = responseMessageDelegate;
    }

    public override async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage responseMessage = await base.ExecuteAsync(cancellationToken);

        if (_responseMessageDelegate != null)
        {
            _responseMessageDelegate(responseMessage);
        }

        return responseMessage;
    }
}
public类flexiblegegotiatedcontentresult:NegotiatedContentResult
{
私有只读操作_responseMessageDelegate;
public FlexibleNegotiatedContentResult(HttpStatusCode状态码、T内容、IContentNegotiator内容协商器、HttpRequestMessage请求、IEnumerable格式化程序)
:base(状态码、内容、内容谈判者、请求、格式化程序)
{
}
public FlexibleNegotiatedContentResult(HttpStatusCode状态码、T内容、ApiController控制器、操作响应消息删除
new FlexibleNegotiatedContentResult<string>(HttpStatusCode.Created, "Entity created!", controller, response => response.Headers.Location = new Uri("https://myapp.com/api/entity/1"));
HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
responseMessage.Headers.Add("Headername", "Value");
ResponseMessageResult response = new ResponseMessageResult(responseMessage);
return response;
public static class HttpExtentions
{
    public static IHttpActionResult AddHeader(this IHttpActionResult action,
        string headerName, IEnumerable<string> headerValues)
    {
        return new HeaderActionResult(action, headerName, headerValues);
    }

    public static IHttpActionResult AddHeader(this IHttpActionResult action,
        string headerName, string header)
    {
        return AddHeader(action, headerName, new[] {header});
    }

    private class HeaderActionResult : IHttpActionResult
    {
        private readonly IHttpActionResult action;

        private readonly Tuple<string, IEnumerable<string>> header;

        public HeaderActionResult(IHttpActionResult action, string headerName,
            IEnumerable<string> headerValues)
        {
            this.action = action;

            header = Tuple.Create(headerName, headerValues);
        }

        public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            var response = await action.ExecuteAsync(cancellationToken);

            response.Headers.Add(header.Item1, header.Item2);

            return response;
        }
    }
}
    System.Web.HttpContext.Current.Response.Headers.
Add(Microsoft.Net.Http.Headers.HeaderNames.LastModified, DBdateModified.Value.ToString("r"));