我通过GET-Requests提供API控制器服务文件。我正在使用PushStreamContentResponse,效果很好。
我还可以在响应对象上设置Content-Length-Header。
现在我也想支持HEAD-Requests。我已经尝试了http://www.strathweb.com/2013/03/adding-http-head-support-to-asp-net-web-api/虽然这可能有用,但我需要一个解决方案,我不需要实际处理请求:检索文件并将其流式传输既费用昂贵,又需要获取元数据(长度等)实际上是一个无操作。
但是,当我尝试设置Content-Length标头时,它将被0覆盖。
我添加了请求跟踪,我看到我的处理程序返回的消息显示正确的URL,Content-Disposition和Content-Length。
我也尝试过使用自定义HttpResponse并实现TryComputeLength。虽然确实调用了这个方法,但结果会在管道中的某个点被丢弃。
有没有办法使用Web API支持这个?
答案 0 :(得分:4)
虽然这可能是2015年,今天(2017年以后)的问题,但您可以这样做
[RoutePrefix("api/webhooks")]
public class WebhooksController : ApiController
{
[HttpHead]
[Route("survey-monkey")]
public IHttpActionResult Head()
{
return Ok();
}
[HttpPost]
[Route("survey-monkey")]
public IHttpActionResult Post(object data)
{
return Ok();
}
}
HEAD api/webhooks/survey-monkey
和POST api/webhooks/survey-monkey
工作得很好。
(这是我刚刚为实现SurveyMonkey的webhooks所做的存根)
答案 1 :(得分:3)
最后,它非常简单。
默认情况下,WebAPI将禁用StreamContent和PushStreamContent的输出缓冲。但是,可以通过Application_Startup替换WebHostBufferPolicySelector来覆盖此行为:
GlobalConfiguration.Configuration.Services.Replace(typeof (IHostBufferPolicySelector), new BufferlessHostBufferPolicySelector());
答案 2 :(得分:1)
另一种解决方案是创建一个自定义HttpContent
来为您完成这项工作。如果您要遵循准则,则还需要自定义的IHttpActionResult
。
假设您有一个控制器,它针对给定的资源返回HEAD
动作,如下所示:
[RoutePrefix("resources")]
public class ResourcesController : ApiController
{
[HttpHead]
[Route("{resource}")]
public IHttpActionResult Head(string resource)
{
// Get resource info here
var resourceType = "application/json";
var resourceLength = 1024;
return Head(resourceType , resourceLength);
}
}
我想出的解决方案如下:
负责人
internal abstract class HeadBase : IHttpActionResult
{
protected HttpStatusCode Code { get; set; } = HttpStatusCode.OK;
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
try
{
response = new HttpResponseMessage(Code)
{
Content = new EmptyContent()
};
FillContentHeaders(response.Content.Headers);
return Task.FromResult(response);
}
catch (Exception)
{
response?.Dispose();
// Good place to log here
throw;
}
}
protected abstract void FillContentHeaders(HttpContentHeaders contentHeaders);
}
// For current need
internal class Head : HeadBase
{
public Head(string mediaType, long contentLength)
{
FakeLength = contentLength;
MediaType = string.IsNullOrWhiteSpace(mediaType) ? "application/octet-stream" : mediaType;
}
protected long FakeLength { get; }
protected string MediaType { get; }
protected override void FillContentHeaders(HttpContentHeaders contentHeaders)
{
contentHeaders.ContentLength = FakeLength;
contentHeaders.ContentType = new MediaTypeHeaderValue(MediaType);
}
}
空白内容
internal sealed class EmptyContent : HttpContent
{
public EmptyContent() : this(null, null)
{
}
public EmptyContent(string mediaType, long? fakeContentLength)
{
if (string.IsNullOrWhiteSpace(mediaType)) mediaType = Constant.HttpMediaType.octetStream;
if (fakeContentLength != null) Headers.ContentLength = fakeContentLength.Value;
Headers.ContentType = new MediaTypeHeaderValue(mediaType);
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
// Necessary to force send
stream?.WriteByte(0);
return Task.FromResult<object>(null);
}
protected override bool TryComputeLength(out long length)
{
length = Headers.ContentLength.HasValue ? Headers.ContentLength.Value : -1;
return Headers.ContentLength.HasValue;
}
}
缓冲策略选择器
internal class HostBufferPolicySelector : IHostBufferPolicySelector
{
public bool UseBufferedInputStream(object hostContext)
{
if (hostContext == null) throw new ArgumentNullException(nameof(hostContext));
return true;
}
public bool UseBufferedOutputStream(HttpResponseMessage response)
{
if (response == null) throw new ArgumentNullException(nameof(response));
if (StringComparer.OrdinalIgnoreCase.Equals(response.RequestMessage.Method.Method, HttpMethod.Head.Method)) return false;
var content = response.Content;
if (content == null) return false;
// If the content knows, then buffering is very likely
var contentLength = content.Headers.ContentLength;
if (contentLength.HasValue && contentLength.Value >= 0) return false;
var buffering = !(content is StreamContent ||
content is PushStreamContent ||
content is EmptyContent);
return buffering;
}
}
应将缓冲策略设置为public static void Register(HttpConfiguration config)
中调用的Application_Start()
方法,
像这样:
config.Services.Replace(typeof(IHostBufferPolicySelector), new HostBufferPolicySelector());
还要检查服务器是否配置为接受HEAD
!
此解决方案有几个优点:
HEAD
是通过WebAPI API处理的我创建了一个Web API 2文件存储控制器,该控制器通过类似的机制支持HEAD
。
感谢Henning Krause的问题,以及answer导致我进入的问题。