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+本文の組み合わせです。Request.Methodを呼び出すことで取得できるメソッドとRequest.RequestUri.ToString()を呼び出すことで取得できるURLですが、本体がPaymentRequestオブジェクトに自動的に逆シリアル化されるの状態では取得できません。 asp.netMVC4フレームワーク。

私の最初の試み: Request.Content.ReadAsStringAsync()。Resultは何も返さないことを理解したので、これは、コンテンツを1回しか読み取れないためです。

2回目の試行: シリアル化してJSON文字列に戻そうとしました。

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

これに伴う問題は、フォーマットが署名の本文部分とわずかに異なることが判明することです。同じデータですが、スペースが少しあります。

これはサードパーティのコンポーネントであるため、Put-methodの呼び出し元の動作を変更することはできません。私は何をすべきか?

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; }
}

次に、コントローラーのEntityプロパティを各投稿のJsonペイロードに設定し、アクションを実行しました。最後に、ActionFilterAttribute.OnActionExecutedオーバーライドされたメソッド内のエンティティデータを取得し、MongoDBに挿入する前にそれをJsonにシリアル化しました。

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、など)...そしてC#ではなくVisualBasicが使用されている場合...

Anders Arpiのコメントと同様に、私は提供されたDarinの回答を使用できませんでした(明らかに私の回答は彼からの直接の派生ですが)。私の場合、Request.Propertiesはオプションではありませんでしたが、Request.InputStreamはオプションでした。これは、最初の段落で述べたように、プロジェクトのタイプに強く依存していると思います。私のソリューションはVS2017でうまくいきました。

同様の回答もページにあります:ASP.NETで生の要求本文を取得する方法は? しかし、これらは私にはうまくいきませんでした。ただし、次のような重要な手がかりを提供しました。'リクエストオブジェクトはBeginRequestイベントに入力されません。イベントのライフサイクルの後半でこのオブジェクトにアクセスする必要があります。

HTTPRequestsの構築/分析の経験がある場合は、この次の段落をスキップしてください。

振り返ってみると非常に明白ですが、リクエスト本文のコンテンツは特定のHTTPリクエストに対してのみ表示されます。GETを実行しているだけの場合(たとえば、新しいページに移動するナビゲーションタブをクリックする場合)、通常、リクエスト本文はありません。したがって、ソリューションをテストするときは、RequestBodyを含む必要があるHttpRequestを確認してください。私の場合、UIから実際にデータを受信して​​いる[送信]ボタンは、RequestBodyを含む投稿であるため、存在します(長さが0より大きい)。FiddlerまたはBurpSuiteを使用していない場合、またはShibbolethを使用している場合(Fiddler / BurpSuiteのテストが複雑になります)、Firefox開発者ツール(F12経由)でHTTPRequestを確認できます。リクエストボディの呼び出しは、Paramsおよび/またはRequestPayloadと呼ばれます。

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

}

コピー後にInputStreamをリセットするために、「ReinstateMonicaCellio」の推奨事項を使用していることに注意してください。

于 2020-02-12T17:21:39.427 に答える