如何获取已读取的内容

时间:2012-08-10 12:42:36

标签: c# asp.net asp.net-web-api

我有一个继承自ApiController的类。它有一个像这样的Put方法:

[PUT("user/{UserId}")]
public HttpResponseMessage Put(string userId, PaymentRequest paymentRequest)
{
    // Calling business logic and so forth here
    // Return proper HttpResponseMessage here
}

该方法在上面工作正常。现在我需要验证方法调用的签名,但在这里我遇到了一个问题。签名本质上是方法+ url + body的组合。我可以通过调用Request.Method和我可以通过调用Request.RequestUri.ToString()得到的url来获得这个方法,但我无法抓住正文,因为 之前 它被asp.net MVC4框架自动反序列化为PaymentRequest对象。

我的第一次尝试: 正如我现在已经理解了Request.Content.ReadAsStringAsync()。结果什么都不返回。这是因为内容只能读一次。

我的第二次尝试: 我试图将其序列化为JSON字符串。

var serializer = new JavaScriptSerializer();
var paymentRequestAsJson = serializer.Serialize(paymentRequest);

问题在于格式与签名的正文部分略有不同。它具有相同的数据,但有一些空格。

我无法更改Put-method的调用者所做的事情,因为这是第三方组件。我该怎么办?

4 个答案:

答案 0 :(得分:33)

您可以从基础请求中读取:

using (var stream = new MemoryStream())
{
    var context = (HttpContextBase)Request.Properties["MS_HttpContext"];
    context.Request.InputStream.Seek(0, SeekOrigin.Begin);
    context.Request.InputStream.CopyTo(stream);
    string requestBody = Encoding.UTF8.GetString(stream.ToArray());
}

答案 1 :(得分:16)

不要在签名中包含body参数,这样您就可以根据需要缓冲内容并多次阅读内容。

[PUT("user/{UserId}")]
public HttpResponseMessage Put(string userId)
{
    Request.Content.LoadIntoBufferAsync().Wait();
    var paymentRequest = Request.Content.ReadAsAsync<PaymentRequest>().Result;
    var requestBody = Request.Content.ReadAsStringAsync().Result;
    // Calling business logic and so forth here
    // Return proper HttpResponseMessage here
}

答案 2 :(得分:1)

我的答案是达林·迪米特洛夫(Darin Dimitrov)的上述...在C#的所有变体(ASP.NET,Core,MVC等)中都应该有变体答案...也许是Visual Studio的年份(2017年) ,2019等)以及是否使用Visual Basic而不是C#...

类似于安德斯·阿皮(Anders Arpi)的评论,我无法使用所提供的达林的答案(尽管显然我的答案是他的直接推导)。在我的情况下,不是Request.Properties,而是Request.InputStream。我怀疑这在很大程度上取决于第一段中提到的项目类型。我的解决方案在VS2017中对我有用。

页面上也有类似的答案:How to get raw request body in ASP.NET? 但是,这些对我没有用。但是,它们确实提供了重要的线索,例如'BeginRequest事件中未填充请求对象。您需要在事件生命周期的稍后阶段访问此对象'...

如果您有HTTPRequests构建/分析方面的经验,请跳过下一段。

尽管回想起来很明显,但请求正文内容仅对某些HTTPRequests出现。如果您只是在进行GET操作(例如,单击导航选项卡将您转到新页面),则通常没有请求正文。因此,在测试解决方案时,请确保您正在查看应包含RequestBody的HttpRequest。在我的情况下,实际上从UI接收数据的任何Submit按钮都将是一个包含RequestBody的Post,因此存在(长度大于0)。如果您没有Fiddler或BurpSuite,或者正在通过Shibboleth(这会增加Fiddler / BurpSuite测试的复杂性),通常可以在Firefox开发人员工具中(通过F12)看到HTTPRequest。调用请求正文称为参数和/或请求有效负载。

Application_AuthenticateRequest在Global.asax.cs中。

(in addition to other using statements)
using System.IO;
using System.Text;
using System.Web;  

protected void Application_AuthenticateRequest(object sender, EventArgs e){

HttpApplication app = (HttpApplication)sender;
// Do a quick check to make sure that we are not on local. 
// I've had some odd errors on local, though I suspect general implementation will not have this issue.
// So if we're actually on a 'real' (a.k.a. non-local) website, I do the following.

      using (var stream = new MemoryStream())
                {
                    app.Request.InputStream.Seek(0, SeekOrigin.Begin);
                    app.Request.InputStream.CopyTo(stream);
                    string requestBody = Encoding.UTF8.GetString(stream.ToArray());
                    app.Request.InputStream.Seek(0, SeekOrigin.Begin); 
                    if (requestBody.Length > 0) Response.Write("requestBody: " + requestBody + "<br>");
                }

}

请注意,我在复制后使用“恢复Monica Cellio”的建议来重置InputStream。

答案 3 :(得分:0)

响应非常延迟,但最近我遇到了同样的挑战。

我已经接近了一点点不同而无需从httpContext获取数据(对于高容量事务Web应用程序来说可能非常昂贵)。

我创建了一个简单的界面,并使每个Controller实现它:

public interface IBaseControllerData
{
    object Entity { get; set; }
}

然后我将Controller的Entity属性设置为每个帖子的Json有效负载并执行操作。最后,我在ActionFilterAttribute.OnActionExecuted重写方法中检索了Entity数据,并在注入MongoDB之前将其序列化为Json:

object entity = ((IBaseControllerData)actionExecutedContext.ActionContext.ControllerContext.Controller).Entity;
                    requestBody = Newtonsoft.Json.JsonConvert.SerializeObject(entity);

希望有所帮助!

干杯