WCF POSTサービスのHTTP RESTリクエスト本文にアクセスするにはどうすればよいですか?
サービスの定義は次のとおりです。
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint")]
MyData GetData();
}
実装は次のとおりです。
public MyData GetData()
{
return new MyData();
}
次のコードを使用してHTTPリクエストにアクセスすることを考えました。
IncomingWebRequestContext context = WebOperationContext.Current.IncomingRequest;
ただし、IncomingWebRequestContextは、本体へのアクセスではなく、ヘッダーへのアクセスのみを許可します。
ありがとう。
WebOperationContextを含まないと私が思う最良の方法
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint", BodyStyle = WebMessageBodyStyle.Bare)]
MyData GetData(System.IO.Stream pStream);
使用する
OperationContext.Current.RequestContext.RequestMessage
回答が遅れて申し訳ありませんが、リクエストの本文を取得するためにUriTemplateパラメーターで機能するものを追加すると思いました。
[ServiceContract]
public class Service
{
[OperationContract]
[WebInvoke(UriTemplate = "{param0}/{param1}", Method = "POST")]
public Stream TestPost(string param0, string param1)
{
string body = Encoding.UTF8.GetString(OperationContext.Current.RequestContext.RequestMessage.GetBody<byte[]>());
return ...;
}
}
body
には、メッセージ本文の未加工のバイトからの文字列が割り当てられます。
上記の答えは、私がこの解決策を思いつくのに役立ちました。名前と値のペアを持つjsonを受け取っています。 {"p1":7514、 "p2":3412、 "p3": "joe smith" ...}
[OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Json
)]
public Stream getJsonRequest()
{
// Get the raw json POST content. .Net has this in XML string..
string JSONstring = OperationContext.Current.RequestContext.RequestMessage.ToString();
// Parse the XML string into a XML document
XmlDocument doc = new XmlDocument();
doc.LoadXml(JSONstring);
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
node.Name // has key
node.InnerText; // has value
WCFはトランスポートプロトコルに依存しないように設計されているため、サービスメソッドはデフォルトでHTTP固有の情報へのアクセスを提供しないようです。ただし、サービスが実際にHTTP経由で公開されることを意図していることを本質的に指定できる「ASP.Net互換モード」について説明した素晴らしい記事に出くわしました。
http://blogs.msdn.com/b/wenlong/archive/2006/01/23/516041.aspx
aspNetCompatibilityEnabled
構成をWeb.config
に追加し、AspNetCompatibilityRequirements
属性を目的のサービス操作に組み合わせると、うまくいくはずです。これを自分で試します。
ホービン
前の回答に対する私の謝罪、私は愚かにもWebOperationContextをキャストしてOperationContextに到達したばかりだと思いましたが、残念ながら実際の答えははるかに醜いです。
これの前置きとして、もっと良い方法があるはずです!
最初に、既存のOperationContextオブジェクトにアタッチできる独自のコンテキストオブジェクトを作成しました。
public class TMRequestContext : IExtension<OperationContext> {
private OperationContext _Owner;
public void Attach(OperationContext owner) {
_Owner = owner;
}
public void Detach(OperationContext owner) {
_Owner = null;
}
public static TMRequestContext Current {
get {
if (OperationContext.Current != null) {
return OperationContext.Current.Extensions.Find<TMRequestContext>();
} else {
return null;
}
}
}
}
この新しいコンテキストオブジェクトにアクセスできるようにするには、現在のコンテキストオブジェクトの拡張機能として追加する必要があります。私はメッセージインスペクタークラスを作成することによってそれを行いました。
public class TMMessageInspector : IDispatchMessageInspector {
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {
OperationContext.Current.Extensions.Add(new TMRequestContext());
return null;
}
}
メッセージインスペクタを機能させるには、新しい「動作」を作成する必要があります。次のコードを使用してこれを行いました。
public class TMServerBehavior : IServiceBehavior {
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) {
//Do nothing
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) {
foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers) {
foreach (EndpointDispatcher epDisp in chDisp.Endpoints) {
epDisp.DispatchRuntime.MessageInspectors.Add(new TMMessageInspector());
}
}
}
}
新しいホストを作成し、OnOpeningメソッドで動作オブジェクトを手動で追加することで、構成ファイルに追加できる動作が必要です。結局、これらのクラスは、OperationContextオブジェクトにアクセスするだけではありません。エラー処理やhttpリクエストオブジェクトへのアクセスなどをログに記録して上書きするために使用しました。したがって、それは見た目ほどばかげた解決策ではありません。ほとんどですが、完全ではありません!
OperationContext.Currentに直接アクセスできなかった理由を本当に覚えていません。常に空であり、この厄介なプロセスが、実際に有効なデータを含むインスタンスを取得する唯一の方法であったという、かすかな思い出があります。
これが私がしたことです:
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;
namespace YourSpaceName
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class YourClassName
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "YourMethodName({id})", BodyStyle = WebMessageBodyStyle.Bare)]
public Stream YourMethodName(Stream input, string id)
{
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("Content-Type", "application/json");
string response = $@"{{""status"": ""failure"", ""message"": ""Please specify the Id of the vehicle requisition to retrieve."", ""d"":null}}";
try
{
string response = (new StreamReader(input)).ReadToEnd();
}
catch (Exception ecp)
{
response = $@"{{""status"": ""failure"", ""message"": ""{ecp.Message}"", ""d"":null}}";
}
return new MemoryStream(Encoding.UTF8.GetBytes(response));
}
}
}
このコードは、単に入力を読み取り、それを書き出します。 POSTリクエストの本文は、変数名に関係なく入力に自動的に割り当てられます。ご覧のとおり、UriTemplateに変数を含めることができます。