我有一个继承自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的调用者所做的事情,因为这是第三方组件。我该怎么办?
答案 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);
希望有所帮助!
干杯