21

我有一个继承自 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,但是我无法像它被自动反序列化为 PaymentRequest 对象之前那样获取正文asp.net MVC4 框架。

我的第一次尝试: 正如我现在所理解的那样, Request.Content.ReadAsStringAsync().Result 什么也不返回。这是因为内容只能读取一次。

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

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

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

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

4

4 回答 4

43

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

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());
}
于 2012-08-10T13:00:17.227 回答
20

不要在签名中包含 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
}
于 2012-08-10T20:25:58.737 回答
1

一个非常延迟的响应,但最近我有同样的挑战要克服。

我已经处理了一些不同的问题,而不必从 httpContext 获取数据(对于大容量事务 Web 应用程序来说可能非常昂贵)。

我创建了一个简单的接口并让每个控制器实现它:

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

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

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

希望有帮助!

干杯

于 2016-10-30T05:48:24.997 回答
1

我的答案是上述 Darin Dimitrov 的变体......所有 C# 变体(ASP.NET、Core、MVC 等)的变体答案应该是预期的......也许是 Visual Studio 的一年(2017 年、2019 年,等)...如果使用 Visual Basic 而不是 C#...

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

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

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

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

注意 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>");
                }

}

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

于 2020-02-12T17:21:39.427 回答