2009-12-04 6 views
9

私は現在、WCF RESTfulサービスを開発中です。 POSTデータの検証の中で、リクエストXMLがビジネスルールに準拠していない場合は、例外がスローされます。WCF + REST:要求データはどこにありますか?

目的は無効と見なされるリクエストがあった場合に適切なスタッフに電子メールを送信することです。しかし、受信したリクエストヘッダー、メソッド、URIとともに、投稿されたXMLも送信したいと思います。

私はこのデータにアクセスする方法を見つけることができませんでした。私はそれにアクセスする機会があるか、何か不足している前に、WCFは実際に要求本文/データを破壊していますか?

なぜ私はリクエストデータにアクセスできないのか混乱しているので、あなたの助けに感謝します。

答えて

9

これは残念なことにサポートされていません。同様のニーズがあり、内部のメンバーをリフレクションで呼び出すことでこれを行いました。私たちはエラーハンドラでそれを使用しています(rawリクエストをダンプすることができるように)。しかし正常に動作します。私は、あなたが所有していないシステム(例えば、このコードを顧客に出荷しない)に対しては、サービスパックなどでいつでも変更できるため、このシステムをお勧めしません。だから、

public static string GetRequestBody() 
{ 
    OperationContext oc = OperationContext.Current; 

    if (oc == null) 
     throw new Exception("No ambient OperationContext."); 

    MessageEncoder encoder = oc.IncomingMessageProperties.Encoder; 
    string contentType = encoder.ContentType; 
    Match match = re.Match(contentType); 

    if (!match.Success) 
     throw new Exception("Failed to extract character set from request content type: " + contentType); 

    string characterSet = match.Groups[1].Value; 

    object bufferedMessage = operationContextType.InvokeMember("request", 
     BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField, 
     null, oc, null); 

    //TypeUtility.AssertType(bufferedMessageType, bufferedMessage); 

    object messageData = bufferedMessageType.InvokeMember("MessageData", 
     BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty, 
     null, bufferedMessage, null); 

    //TypeUtility.AssertType(jsonBufferedMessageDataType, messageData); 

    object buffer = jsonBufferedMessageDataType.InvokeMember("Buffer", 
     BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty, 
     null, messageData, null); 

    ArraySegment<byte> arrayBuffer = (ArraySegment<byte>)buffer; 

    Encoding encoding = Encoding.GetEncoding(characterSet); 

    string requestMessage = encoding.GetString(arrayBuffer.Array, arrayBuffer.Offset, arrayBuffer.Count); 

    return requestMessage; 
} 
+1

聖なる牛!それは私の解決策よりもさらに悪いです:-) –

+0

合意 - メリットは、すべての要求に対してメッセージインスペクタを使用してトランスポート層から2番目のコピーを密輸する必要がないことです。こうすることで、サービスコードから直接元のバッファにアクセスすることができます。したがって私の元の注意。 :)私は彼らがWebOperationContextからそれを公開することを望んでいますが、それを分けて、なぜ彼らは(なぜあなたが任意のサイズのストリーミングされた要求を考慮する場合)特にそうでないのか分かります。 – nitzmahone

+0

返信いただきありがとうございます。私は今あなたがこのアプローチをとっている理由を理解しています。 WCFがどのように機能するのかを理解するには、実装を掘り下げる必要があります。それは、複雑さを抽象化しようとする目的を打ち消すものです! –

9

あなたのようなあなたの契約何か宣言した場合、:(あなたの代わりにXMLを使用することができます)

[WebInvoke(Method = "POST", UriTemplate = "create", ResponseFormat=WebMessageFormat.Json)] 
int CreateItem(Stream streamOfData); 

を streamOfDataは、HTTP POSTのボディでなければなりません。あなたは次のようなものを使って逆シリアル化することができます:

StreamReader reader = new StreamReader(streamId); 
String res = reader.ReadToEnd(); 
NameValueCollection coll = HttpUtility.ParseQueryString(res); 

それは少なくとも私たちのためにそれのように働いています。文字列をXMLDocumentなどに取得するには、別の方法を使用することができます。これはJSONの投稿に有効です。最も洗練されたソリューションではないかもしれませんが、機能しています。

こちらがお役に立てば幸いです。

using (var reader = OperationContext.Current.RequestContext.RequestMessage.GetReaderAtBodyContents()) { 
    if (reader.Read()) 
     return new string (Encoding.ASCII.GetChars (reader.ReadContentAsBase64())); 
       return result; 
    } 
} 

読者はHttpStreamXmlDictionaryReader(それは私の場合にあったように)、ある場合:

グレン

+1

グレン、ご返信ありがとうございます。私は現在、投稿されたXMLをオブジェクトに直ちに逆シリアル化する操作規約を持っています。新しいオブジェクトを処理するためのアクセス権があるにもかかわらず、私は依然として生のリクエストを利用できるようにしたいと思います。体の単純な文字列表現。ありがとう! – RossG

2

OperationContext.Current.RequestContext.RequestMessage 
2

は、ここでは、反射せずにそれを行う方法です、これを試してみてくださいクラスのメソッドReadContentAsBase64(byte[] buffer, int index, int count)の実装は、単にこれらのパラメータをメソッドStream.Reaに渡します。

私がbyte[]を取得すると、バイトをASCIIエンコーディングで文字列に変換します。適切な実装のために、メッセージのヘッダからのコンテンツタイプ&エンコーディングをHTTP仕様に従って行うことができます。

+0

Message.Stateが既に読み込みに設定されている場合、ソリューションが機能しません - InvalidOperationExceptionが発生します。「このメッセージは読み取られたため操作をサポートできません。 – Dai

+0

これは次のように処理されます。 http://stackoverflow.com/questions/2184806/read-wcf-message-body-twice-message-cannot-beread –

0

WCFサービスのカスタムHttpModuleでHttpApplication.Request.InputStrea mを逮捕し、ストリームを読み取り、カスタムHttpModuleのイベントハンドラでその位置を再び0に設定できます。その後、セッションに格納し、実際のOperationContractにさらにアクセスします。例えば

public class CustomModule : IHttpModule 
{ 
    public void Dispose() 
    { 

    } 

    public void Init(HttpApplication context) 
    { 
     context.AcquireRequestState +=context_AcquireRequestState; 
    } 

    void context_AcquireRequestState(object sender, EventArgs e) 
    { 
     HttpApplication application = sender as HttpApplication; 
     Stream str = application.Request.InputStream; 
     StreamReader sr = new StreamReader(str); 
     string req = sr.ReadToEnd(); 
     str.Position = 0; 
     application.Session["CurrentRequest"] = req; 
    } 
}