为什么读取一次一个Web API请求的主体? [英] Why is the body of a Web API request read once?
问题描述
我的目标是验证使用AuthorizationFilter或DelegatingHandler的Web API请求。我想寻找在几个地方,包括请求主体的客户端ID和身份验证令牌。起初它似乎像这将是容易的,我可以做这样的事情。
My goal is to authenticate Web API requests using a AuthorizationFilter or DelegatingHandler. I want to look for the client id and authentication token in a few places, including the request body. At first it seemed like this would be easy, I could do something like this
var task = _message.Content.ReadAsAsync<Credentials>();
task.Wait();
if (task.Result != null)
{
// check if credentials are valid
}
的问题是,在HttpContent只能被读取一次。如果我这样做了处理程序或过滤器则内容不适用于我在我的操作方法。我发现了几个答案在这里计算器,像这样的:<一href=\"http://stackoverflow.com/questions/12494067/read-httpcontent-in-mvc-webapi-controller?rq=1\">Read HttpContent MVC中的WebAPI控制器的解释,这是故意这样,但他们不说为什么。这似乎是一个pretty严格的限制,阻止我使用任何酷网API内容解析$ C $在过滤器或处理程序的C
The problem is that the HttpContent can only be read once. If I do this in a Handler or a Filter then the content isn't available for me in my action method. I found a few answers here on StackOverflow, like this one: Read HttpContent in MVC webApi controller that explain that it is intentionally this way, but they don't say WHY. This seems like a pretty severe limitation that blocks me from using any of the cool Web API content parsing code in Filters or Handlers.
它是一个技术上的限制?难道是想阻止我这样做,我没有看到一个非常糟糕的事情(TM)?
Is it a technical limitation? Is it trying to keep me from doing a VERY BAD THING(tm) that I'm not seeing?
死后:
我接过一看像源菲利普建议。 ReadAsStreamAsync返回内部流并没有什么东西调用seek 阻止你,如果流支持它。在我的测试,如果我叫ReadAsAsync那么这样做:
I took a look at the source like Filip suggested. ReadAsStreamAsync returns the internal stream and there's nothing stopping you from calling Seek if the stream supports it. In my tests if I called ReadAsAsync then did this:
message.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.Seek(0, SeekOrigin.Begin)).Wait();
自动模型绑定过程将正常工作,当它击中了我的操作方法。我尽管没有使用此,我选择了一些更直接的:
The automatic model binding process would work fine when it hit my action method. I didn't use this though, I opted for something more direct:
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;
通过一个扩展方法(我在.NET 4.0中没有的await关键字)
With an extension method (I'm in .NET 4.0 with no await keyword)
public static class TaskExtensions
{
public static T WaitFor<T>(this Task<T> task)
{
task.Wait();
if (task.IsCanceled) { throw new ApplicationException(); }
if (task.IsFaulted) { throw task.Exception; }
return task.Result;
}
}
最后一抓,HttpContent有一个硬codeD最大缓冲区大小:
One last catch, HttpContent has a hard-coded max buffer size:
internal const int DefaultMaxBufferSize = 65536;
所以,如果你的内容将是大于,你需要手动调用LoadIntoBufferAsync具有更大的尺寸您尝试调用ReadAsByteArrayAsync之前。
So if your content is going to be bigger than that you'll need to manually call LoadIntoBufferAsync with a larger size before you try to call ReadAsByteArrayAsync.
推荐答案
您指出答案是不完全准确。
The answer you pointed to is not entirely accurate.
您可以随时读取为字符串( ReadAsStringAsync
)或字节[]( ReadAsByteArrayAsync
),因为它们缓冲要求内部。
You can always read as string (ReadAsStringAsync
)or as byte[] (ReadAsByteArrayAsync
) as they buffer the request internally.
例如下面的虚拟处理器:
For example the dummy handler below:
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);
}
}
同样适用于字节]:
Same applies to byte[]:
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);
}
}
每个不会导致贴内容为空时其到达控制器。
Each will not cause the posted content to be null when it reaches the controller.
这篇关于为什么读取一次一个Web API请求的主体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!