Asp.net 为什么Web API请求的主体只读取一次?

Asp.net 为什么Web API请求的主体只读取一次?,asp.net,asp.net-web-api,Asp.net,Asp.net Web Api,我的目标是使用AuthorizationFilter或DelegatingHandler对Web API请求进行身份验证。我想在几个地方寻找客户机id和身份验证令牌,包括请求主体。起初,这似乎很容易,我可以做这样的事情 var task = _message.Content.ReadAsAsync<Credentials>(); task.Wait(); if (task.Result != null) { // check if credentials are vali

我的目标是使用AuthorizationFilter或DelegatingHandler对Web API请求进行身份验证。我想在几个地方寻找客户机id和身份验证令牌,包括请求主体。起初,这似乎很容易,我可以做这样的事情

var task = _message.Content.ReadAsAsync<Credentials>();

task.Wait();

if (task.Result != null)
{
    // check if credentials are valid
}
当自动模型绑定过程遇到我的动作方法时,它会很好地工作。但我没有用这个,我选择了更直接的方式:

var buffer = new MemoryStream(_message.Content.ReadAsByteArrayAsync().WaitFor());
var formatters = _message.GetConfiguration().Formatters;
var reader = formatters.FindReader(typeof(Credentials), _message.Content.Headers.ContentType);
var credentials = reader.ReadFromStreamAsync(typeof(Credentials), buffer, _message.Content, null).WaitFor() as Credentials;
使用扩展方法(我在.NET4.0中,没有等待关键字)


因此,如果您的内容将大于此值,则在尝试调用ReadAsByteArrayAsync之前,需要手动调用更大的LoadIntoBufferAsync。

您指出的答案并不完全准确

当它们在内部缓冲请求时,您始终可以读取字符串(
readastringasync
)或字节[](
ReadAsByteArrayAsync

例如,下面的虚拟处理程序:

public class MyHandler : DelegatingHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var body = await request.Content.ReadAsStringAsync();
        //deserialize from string i.e. using JSON.NET

        return base.SendAsync(request, cancellationToken);
    }
}
公共类MyHandler:DelegatingHandler
{
受保护的重写异步System.Threading.Tasks.Task SendAsync(HttpRequestMessage请求,System.Threading.CancellationToken CancellationToken)
{
var body=wait request.Content.ReadAsStringAsync();
//从字符串反序列化,即使用JSON.NET
返回base.sendaync(请求、取消令牌);
}
}
同样适用于字节[]:

public class MessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var requestMessage = await request.Content.ReadAsByteArrayAsync();
        //do something with requestMessage - but you will have to deserialize from byte[]

        return base.SendAsync(request, cancellationToken);
    }
}
public类MessageHandler:DelegatingHandler
{
受保护的覆盖异步任务SendAsync(HttpRequestMessage请求,CancellationToken CancellationToken)
{
var requestMessage=wait request.Content.ReadAsByteArrayAsync();
//对requestMessage执行一些操作-但必须从字节[]反序列化
返回base.sendaync(请求、取消令牌);
}
}

每一个都不会导致发布的内容在到达控制器时为空。

我会将clientId和身份验证密钥放在标题中,而不是内容中


通过这种方式,你可以随意阅读

谢谢你的回复!我在做测试时确实注意到了这一点。我对此有两个担忧。1.我还可以使用HttpContext直接获取流。我真的希望利用Web API中的功能将格式化程序与媒体类型相匹配,这样,如果正文是XML、JSON或只是一个标准表单submit,我就不必采取不同的行为。2.这些“ReadAsAsync”方法有不同的副作用这一事实让我担心。我找不到这种差异。我可以信赖它吗?HttpContent的不同行为不是Web API的一个功能,而是更早发布的System.Net.Http的一个功能-我认为这就是为什么没有人想到为Web API显式地记录它。在后台读取字符串/字节[]所做的是调用LoadIntoBufferAsync,我还建议您检查HttpContent的符号源。在其他情况下,就像使用MediaTypeFormatters一样,您显式地使用流,并且由于您将流的位置向前移动(并且它是不可倒带的),因此模型绑定器将始终从中获取空值。是的,您可以信赖它,它不是一个黑客,这种行为是按设计的:-)感谢您的回答Filip,这真的为我节省了很多时间!非常感谢:)@FilipW如果是的话,你能解释一下什么时候不能读两遍吗?谢谢你的建议!我确实考虑了报头,但看起来我必须为此制定一些非标准的HTTP报头参数,我不确定对于各种平台上的各种HTTP客户端API来说,支持它是多么容易。我当然有可能太快就放弃了这个选择,我同意。如果它很小,自定义标题就可以了。如果它是一个“whacko”字符串..您也可以将其设置为base64字符串。再说一次。如果它很小。虽然你找到了一种方法可以多次阅读尸体,但你们发现为什么不能读两遍了吗?因为我真的很想弄清楚为什么会有这样的限制。
internal const int DefaultMaxBufferSize = 65536;
public class MyHandler : DelegatingHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var body = await request.Content.ReadAsStringAsync();
        //deserialize from string i.e. using JSON.NET

        return base.SendAsync(request, cancellationToken);
    }
}
public class MessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var requestMessage = await request.Content.ReadAsByteArrayAsync();
        //do something with requestMessage - but you will have to deserialize from byte[]

        return base.SendAsync(request, cancellationToken);
    }
}