【发布时间】:2016-02-23 22:29:28
【问题描述】:
我正在尝试捕获原始请求数据以进行问责,并希望将请求正文内容从请求对象中提取出来。
我看到有人建议做一个 Request.InputStream,但这个方法在 Request 对象上不可用。
知道如何获取 Request.Content 正文的字符串表示形式吗?
【问题讨论】:
标签: c# asp.net-web-api
我正在尝试捕获原始请求数据以进行问责,并希望将请求正文内容从请求对象中提取出来。
我看到有人建议做一个 Request.InputStream,但这个方法在 Request 对象上不可用。
知道如何获取 Request.Content 正文的字符串表示形式吗?
【问题讨论】:
标签: c# asp.net-web-api
在您对@Kenneth 的回答的评论中,您说ReadAsStringAsync() 正在返回空字符串。
那是因为您(或模型绑定器之类的东西)已经阅读了内容,所以 Request.Content 中内部流的位置在末尾。
你可以这样做:
public static string GetRequestBody()
{
var bodyStream = new StreamReader(HttpContext.Current.Request.InputStream);
bodyStream.BaseStream.Seek(0, SeekOrigin.Begin);
var bodyText = bodyStream.ReadToEnd();
return bodyText;
}
【讨论】:
您可以通过在Request.Content 属性上调用ReadAsStringAsAsync 来获取原始数据。
string result = await Request.Content.ReadAsStringAsync();
如果你希望它在一个字节或一个流中,有各种重载。由于这些是异步方法,您需要确保您的控制器是异步的:
public async Task<IHttpActionResult> GetSomething()
{
var rawMessage = await Request.Content.ReadAsStringAsync();
// ...
return Ok();
}
编辑:如果你从这个方法接收到一个空字符串,这意味着其他东西已经读取了它。当它这样做时,它将指针留在最后。另一种方法如下:
public IHttpActionResult GetSomething()
{
var reader = new StreamReader(Request.Body);
reader.BaseStream.Seek(0, SeekOrigin.Begin);
var rawMessage = reader.ReadToEnd();
return Ok();
}
在这种情况下,您的端点不需要是异步的(除非您有其他异步方法)
【讨论】:
ReadAsStringAsync之前是否已经阅读过。这是一个流,此方法将读取到末尾并将指针留在那里。
Request.Content.ReadAsStringAsync().Result
对于其他不想让他们的控制器异步,或者无法访问 HttpContext,或者正在使用 dotnet 核心(这个答案是我在谷歌上找到的第一个尝试这样做的答案)的其他用户,以下对我有用:
[HttpPut("{pathId}/{subPathId}"),
public IActionResult Put(int pathId, int subPathId, [FromBody] myViewModel viewModel)
{
var body = new StreamReader(Request.Body);
//The modelbinder has already read the stream and need to reset the stream index
body.BaseStream.Seek(0, SeekOrigin.Begin);
var requestBody = body.ReadToEnd();
//etc, we use this for an audit trail
}
【讨论】:
Seek 否则你会得到“NotSupportedException: Specified method is not supported. Microsoft.AspNetCore.Server.Kestrel.Core.Internal .Http.HttpRequestStream.Seek"
如果您既需要从请求中获取原始内容,又需要在控制器中使用它的绑定模型版本,您很可能会遇到此异常。
NotSupportedException: Specified method is not supported.
例如,您的控制器可能看起来像这样,让您想知道为什么上面的解决方案不适合您:
public async Task<IActionResult> Index(WebhookRequest request)
{
using var reader = new StreamReader(HttpContext.Request.Body);
// this won't fix your string empty problems
// because exception will be thrown
reader.BaseStream.Seek(0, SeekOrigin.Begin);
var body = await reader.ReadToEndAsync();
// Do stuff
}
您需要将模型绑定从方法参数中取出,然后手动绑定自己:
public async Task<IActionResult> Index()
{
using var reader = new StreamReader(HttpContext.Request.Body);
// You shouldn't need this line anymore.
// reader.BaseStream.Seek(0, SeekOrigin.Begin);
// You now have the body string raw
var body = await reader.ReadToEndAsync();
// As well as a bound model
var request = JsonConvert.DeserializeObject<WebhookRequest>(body);
}
很容易忘记这一点,我以前解决过这个问题,但现在不得不重新学习解决方案。希望我在这里的回答能给自己一个很好的提醒……
【讨论】:
这里是this answer 作为扩展方法:
using System.IO;
using System.Text;
namespace System.Web.Http
{
public static class ApiControllerExtensions
{
public static string GetRequestBody(this ApiController controller)
{
using (var stream = new MemoryStream())
{
var context = (HttpContextBase)controller.Request.Properties["MS_HttpContext"];
context.Request.InputStream.Seek(0, SeekOrigin.Begin);
context.Request.InputStream.CopyTo(stream);
var requestBody = Encoding.UTF8.GetString(stream.ToArray());
return requestBody;
}
}
}
}
【讨论】: