RESTful WCF web service POST problem - c#

I can't pass over parameters to wcf web service. My web method:
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "playersJson2")]
List<Person> GetPlayers(string name1, string name2);
When I make http post request, I got 200 OK response with correct json form but web service seems to fail to get paramters (name1, name2). Wireshark shows following:
Do you see anything wrong?
Update: Not sure it matters but my service is using "webHttpBinding" and post request is coming from Android.

WCF doesn't support forms/encoded data out-of-the box. The other answers mentioned some alternatives (receive the input as a Stream, change the request to JSON). Another alternative, which doesn't force you to change the request or the operation is to use a custom formatter which understands form-urlencoded requests. The code below shows one which does that.
public class MyWebHttpBehavior : WebHttpBehavior
{
protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
bool isRequestWrapped = this.IsRequestWrapped(operationDescription.Behaviors.Find<WebInvokeAttribute>());
IDispatchMessageFormatter originalFormatter = base.GetRequestDispatchFormatter(operationDescription, endpoint);
if (isRequestWrapped)
{
return new MyFormUrlEncodedAwareFormatter(
operationDescription,
originalFormatter,
this.GetQueryStringConverter(operationDescription));
}
else
{
return originalFormatter;
}
}
private bool IsRequestWrapped(WebInvokeAttribute wia)
{
WebMessageBodyStyle bodyStyle;
if (wia.IsBodyStyleSetExplicitly)
{
bodyStyle = wia.BodyStyle;
}
else
{
bodyStyle = this.DefaultBodyStyle;
}
return bodyStyle == WebMessageBodyStyle.Wrapped || bodyStyle == WebMessageBodyStyle.WrappedRequest;
}
class MyFormUrlEncodedAwareFormatter : IDispatchMessageFormatter
{
const string FormUrlEncodedContentType = "application/x-www-form-urlencoded";
OperationDescription operation;
IDispatchMessageFormatter originalFormatter;
QueryStringConverter queryStringConverter;
public MyFormUrlEncodedAwareFormatter(OperationDescription operation, IDispatchMessageFormatter originalFormatter, QueryStringConverter queryStringConverter)
{
this.operation = operation;
this.originalFormatter = originalFormatter;
this.queryStringConverter = queryStringConverter;
}
public void DeserializeRequest(Message message, object[] parameters)
{
if (IsFormUrlEncodedMessage(message))
{
XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
bodyReader.ReadStartElement("Binary");
byte[] bodyBytes = bodyReader.ReadContentAsBase64();
string body = Encoding.UTF8.GetString(bodyBytes);
NameValueCollection pairs = HttpUtility.ParseQueryString(body);
Dictionary<string, string> values = new Dictionary<string, string>();
foreach (var key in pairs.AllKeys)
{
values.Add(key, pairs[key]);
}
foreach (var part in this.operation.Messages[0].Body.Parts)
{
if (values.ContainsKey(part.Name))
{
string value = values[part.Name];
parameters[part.Index] = this.queryStringConverter.ConvertStringToValue(value, part.Type);
}
else
{
parameters[part.Index] = GetDefaultValue(part.Type);
}
}
}
else
{
this.originalFormatter.DeserializeRequest(message, parameters);
}
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
throw new NotSupportedException("This is a request-only formatter");
}
private static bool IsFormUrlEncodedMessage(Message message)
{
object prop;
if (message.Properties.TryGetValue(WebBodyFormatMessageProperty.Name, out prop))
{
if (((WebBodyFormatMessageProperty)prop).Format == WebContentFormat.Raw)
{
if (message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out prop))
{
if (((HttpRequestMessageProperty)prop).Headers[HttpRequestHeader.ContentType].StartsWith(FormUrlEncodedContentType))
{
return true;
}
}
}
}
return false;
}
private static object GetDefaultValue(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
else
{
return null;
}
}
}
}
[ServiceContract]
public class Service
{
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public string Concat(string text1, string text2)
{
return text1 + text2;
}
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public int Add(int x, int y)
{
return x + y;
}
}
class Program
{
public static void SendRequest(string uri, string method, string contentType, string body)
{
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
req.Method = method;
if (!String.IsNullOrEmpty(contentType))
{
req.ContentType = contentType;
}
if (body != null)
{
byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
req.GetRequestStream().Write(bodyBytes, 0, bodyBytes.Length);
req.GetRequestStream().Close();
}
HttpWebResponse resp;
try
{
resp = (HttpWebResponse)req.GetResponse();
}
catch (WebException e)
{
resp = (HttpWebResponse)e.Response;
}
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (string headerName in resp.Headers.AllKeys)
{
Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
}
Console.WriteLine();
Console.WriteLine(new StreamReader(resp.GetResponseStream()).ReadToEnd());
Console.WriteLine();
Console.WriteLine(" *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* ");
Console.WriteLine();
}
static void Main(string[] args)
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "").Behaviors.Add(new MyWebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
SendRequest(baseAddress + "/Add", "POST", "application/json", "{\"x\":22,\"y\":33}");
SendRequest(baseAddress + "/Add", "POST", "application/x-www-form-urlencoded", "x=22&y=33");
SendRequest(baseAddress + "/Add", "POST", "application/json", "{\"x\":22,\"z\":33}");
SendRequest(baseAddress + "/Add", "POST", "application/x-www-form-urlencoded", "x=22&z=33");
SendRequest(baseAddress + "/Concat", "POST", "application/json", "{\"text1\":\"hello\",\"text2\":\" world\"}");
SendRequest(baseAddress + "/Concat", "POST", "application/x-www-form-urlencoded", "text1=hello&text2=%20world");
SendRequest(baseAddress + "/Concat", "POST", "application/json", "{\"text1\":\"hello\",\"text9\":\" world\"}");
SendRequest(baseAddress + "/Concat", "POST", "application/x-www-form-urlencoded", "text1=hello&text9=%20world");
}
}

It looks like you need to parse your post data manual...
You can look here for example

The only thing that looks out of place is the playerJson2 but that is just because I have never used the UriTemplate before. Can you get it to work without the UriTemplate and just post to /WcfService1/Service1.svc/GetPlayers? Are there any other WCF services that you have working in the project?

You need to set the correct Content-Type which is application/json in this case.
Sorry for misreading your question. Update your UriTemplate as follows:
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "playersJson2?name1={name1}&name2={name2}")]

Related

RESTful WCF Service returns a 400 code when sending form-data parameters

i 'm trying to call a post method via Postman
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "Login"]
string Login(string host, string user, string pass, string applicationId);
public string Login(string host, string user, string pass, string applicationId)
{
int res= new login().Loginn(host, user, pass, applicationId);
if (res == 1)
return "Connected";
else
return "Failed";
}
whene i pass those parameters via Postman in body request form-data the response is 400 bad request
whene i change to application/json and i put a json string in raws it works .
is there any solution to use body form-data request please whithout using one parameter with stream type
thank you
You will need to have a custom message formatter that will understand the form-urlencoded input and convert that into the parameters of your operation. The post at https://blogs.msdn.microsoft.com/endpoint/2011/05/03/wcf-extensibility-message-formatters/ has a lot of informations about formatters, and the code below shows a simple implementation of such a formatter that can deal with form data.
public class StackOverflow_35921627
{
[ServiceContract]
public interface ITest
{
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json)]
string Login(string host, string user, string pass, string applicationId);
}
public class Service : ITest
{
public string Login(string host, string user, string pass, string applicationId)
{
return string.Format("Login result: {0} - {1} - {2} - {3}", host, user, pass, applicationId);
}
}
public class MyWebHttpBehavior : WebHttpBehavior
{
protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
var originalFormatter = base.GetRequestDispatchFormatter(operationDescription, endpoint);
return new FormDataDispatchFormatter(operationDescription, originalFormatter);
}
}
public class FormDataDispatchFormatter : IDispatchMessageFormatter
{
IDispatchMessageFormatter originalFormatter;
Dictionary<string, int> parameterNames;
public FormDataDispatchFormatter(OperationDescription operation, IDispatchMessageFormatter originalFormatter)
{
var inputMessageBodyParts = operation.Messages[0].Body.Parts;
this.parameterNames = new Dictionary<string, int>();
for (int i = 0; i < inputMessageBodyParts.Count; i++)
{
if (inputMessageBodyParts[i].Type == typeof(string))
{
// For now, only support string parameters; others will be ignored
this.parameterNames.Add(inputMessageBodyParts[i].Name, i);
}
}
this.originalFormatter = originalFormatter;
}
public void DeserializeRequest(Message message, object[] parameters)
{
object httpRequestProperty;
if (!message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestProperty))
{
throw new InvalidOperationException("This formatter can only be used with HTTP");
}
var contentType = ((HttpRequestMessageProperty)httpRequestProperty).Headers[HttpRequestHeader.ContentType];
if (contentType == null || !contentType.StartsWith("application/x-www-form-urlencoded"))
{
this.originalFormatter.DeserializeRequest(message, parameters);
return;
}
object bodyFormatProperty;
if (!message.Properties.TryGetValue(WebBodyFormatMessageProperty.Name, out bodyFormatProperty) ||
(bodyFormatProperty as WebBodyFormatMessageProperty).Format != WebContentFormat.Raw)
{
throw new InvalidOperationException("Incoming messages must have a body format of Raw. Is a ContentTypeMapper set on the WebHttpBinding?");
}
XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
bodyReader.ReadStartElement("Binary");
byte[] rawBody = bodyReader.ReadContentAsBase64();
MemoryStream ms = new MemoryStream(rawBody);
StreamReader sr = new StreamReader(ms);
string formData = sr.ReadToEnd();
string[] parts = formData.Split('&');
foreach (var part in parts)
{
// TODO: handle url-decoding
var kvp = part.Split('=');
if (kvp.Length != 2)
{
throw new InvalidOperationException("Only support simple parameters for now");
}
int index;
if (this.parameterNames.TryGetValue(kvp[0], out index))
{
parameters[index] = kvp[1];
}
}
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
throw new NotSupportedException("This is a request-only formatter");
}
}
internal static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new MyWebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
SendPostRequest(baseAddress + "/Login", "application/x-www-form-urlencoded", "host=myHost&user=myUser&pass=myPassword&applicationId=app123");
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
static void SendPostRequest(string url, string contentType, string body)
{
HttpWebRequest req = HttpWebRequest.CreateHttp(url);
req.Method = "POST";
req.ContentType = contentType;
byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
Stream reqStream = req.GetRequestStream();
reqStream.Write(bodyBytes, 0, bodyBytes.Length);
reqStream.Close();
HttpWebResponse resp;
try
{
resp = (HttpWebResponse)req.GetResponse();
}
catch (WebException ex)
{
resp = (HttpWebResponse)ex.Response;
}
if (resp == null)
{
Console.WriteLine("Response is null");
}
else
{
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (string headerName in resp.Headers.AllKeys)
{
Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
}
Console.WriteLine();
Stream respStream = resp.GetResponseStream();
if (respStream != null)
{
Console.WriteLine(new StreamReader(respStream).ReadToEnd());
}
else
{
Console.WriteLine("HttpWebResponse.GetResponseStream returned null");
}
}
Console.WriteLine();
Console.WriteLine(" *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* ");
Console.WriteLine();
}
}

How to read body of HttpRequest POSTed to a REST web service - C# WCF

I have an Apex Class (a class in SalesForce) that calls out to a REST web service.
public class WebServiceCallout
{
#future (callout=true)
public static void sendNotification(String aStr)
{
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http http = new Http();
req.setEndpoint('http://xx.xxx.xxx.xx:41000/TestService/web/test');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(aStr); // I want to read this in the web service
try
{
res = http.send(req);
}
catch(System.CalloutException e)
{
System.debug('Callout error: '+ e);
System.debug(res.toString());
}
}
}
The REST web service (C#, WCF) looks like so:
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/test")]
string Test(string aStr);
}
The Test() method does primitive operations.
When I run
WebServiceCallout.sendNotification("a test message")
the POST gets to the web service but how can I read what was set in the body of the HttpRequest req that was set back in the sendNotification() method - req.setBody(aStr);?
That is, what should the parameter of string Test(string aStr); be?
Do I need to specify anything else such as any configs/attributes in my WebInvoke or the App.config (e.g. the binding)?
If you want to read the raw body of the incoming request, you should define the type of the parameter as a Stream, not string. The code below shows one way to implement your scenario, and the post at http://blogs.msdn.com/b/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-receiving-arbitrary-data.aspx has more information on this "raw" mode.
public class StackOverflow_25377059
{
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/test")]
string Test(Stream body);
}
public class Service : ITestService
{
public string Test(Stream body)
{
return new StreamReader(body).ReadToEnd();
}
}
class RawMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
return WebContentFormat.Raw;
}
}
public static void Test()
{
var baseAddress = "http://" + Environment.MachineName + ":8000/Service";
var host = new ServiceHost(typeof(Service), new Uri(baseAddress));
var binding = new WebHttpBinding { ContentTypeMapper = new RawMapper() };
host.AddServiceEndpoint(typeof(ITestService), binding, "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
var req = (HttpWebRequest)HttpWebRequest.Create(baseAddress + "/test");
req.Method = "POST";
req.ContentType = "application/json";
var reqStream = req.GetRequestStream();
var body = "a test message";
var bodyBytes = new UTF8Encoding(false).GetBytes(body);
reqStream.Write(bodyBytes, 0, bodyBytes.Length);
reqStream.Close();
var resp = (HttpWebResponse)req.GetResponse();
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (var header in resp.Headers.AllKeys)
{
Console.WriteLine("{0}: {1}", header, resp.Headers[header]);
}
Console.WriteLine();
Console.WriteLine(new StreamReader(resp.GetResponseStream()).ReadToEnd());
Console.WriteLine();
}
}
By the way, your incoming request is not technically correct - you say (via Content-Type) that you're sending JSON, but the request body (a test message) is not a valid JSON string (it should be wrapped in quotes - "a test message" to be a JSON string instead).

RestSharp JSON Not Deseralizing

I have a RESTful WCF Service function, however it is not serializing properly via the RestSharp Client.
[ServiceContract]
public interface IRestDragon
{
[OperationContract(Name = "getconfig")]
[WebInvoke(Method = "GET", UriTemplate = "getconfig/{id}", BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
DragonConfig GetConfig(string id);
}
public class RestDragon : IRestDragon
{
public DragonConfig GetConfig(string id)
{
var config = new DragonConfig {Name = "Test", Data = "DAtaaaaaa"};
return config;
}
}
And here is how I consume the service:
static void Main(string[] args)
{
Console.ReadLine();
var client = new RestClient("http://localhost:5463/RESTDragon.svc");
client.AddDefaultHeader("ContentType", "application/json");
var request = new RestRequest("/getconfig/11123") {Method = Method.GET, RequestFormat = DataFormat.Json};
var response = client.Execute<DragonConfig>(request);
Console.WriteLine("Response: " + response.Content);
Console.ReadLine();
}
However it is returning:
Response: {"getconfigResult":{"Data":"DAtaaaaaa","Name":"Test"}}
I am unable to access the De-Serialized Data via response.Data.*. It comes back as null, and the data is shown in Content however, in the JSON Format, with the strange getconfigResult identifier.
Setting BodyStyle = WebMessageBodyStyle.Bare corrected the issue.
You need to access response.Data instead of response.Content to get the deserialized object.

WCF REST basic authentication on certain methods

I have quite a few RESTful (GET and POST) methods implemented in WCF 4.0. All these work over SSL.
An example of some of the methods:
[OperationContract]
[WebInvoke(UriTemplate = "Login?", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
LoginResponse Login(LoginRequest request);
[OperationContract]
[WebInvoke(UriTemplate = "UpdateDetails?", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
UpdateUserDetailResponse UpdateDetails(UpdateUserDetailRequest request);
[OperationContract]
[WebInvoke(UriTemplate = "GetDetails?", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
UserDetailResponse GetDetails(UserDetailRequest request);
I have looked through so many blogs and forums and I still cannot find something that meets
my requirements. I need to implement basic authentication on some of the methods but not all. If you look at the examples above I require a username and password to be sent through for the UpdateDetails and GetDetails method, but not for the Login method. The username and password is then authenticated against a database. Is it possible to do something like this?
As a side note: these REST methods are called by many different mobile devices.
I have looked at the following sites and they all implement basic authentication over REST but they cover all the methods mentioned above.
http://msdn.microsoft.com/en-us/library/aa702565.aspx
Adding basic HTTP auth to a WCF REST service
http://custombasicauth.codeplex.com/ (links at the bottom don't work
anymore)
Is it possible to do what I want to do?
I created a BasicAuthenticationInvoker class which you attribute on the methods you would like to authenticate as follows:
[OperationContract]
[BasicAuthenticationInvoker] // this is the auth attribute!
[WebInvoke(UriTemplate = "QuickQuote?", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
QuickQuoteResponse QuickQuote(QuickQuoteRequest request);
The actual class looks as follows:
public class BasicAuthenticationInvoker : Attribute, IOperationBehavior, IOperationInvoker
{
#region Private Fields
private IOperationInvoker _invoker;
#endregion
#region IOperationBehavior Members
public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
_invoker = dispatchOperation.Invoker;
dispatchOperation.Invoker = this;
}
public void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation)
{
}
public void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters)
{
}
public void Validate(OperationDescription operationDescription)
{
}
#endregion
#region IOperationInvoker Members
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
if (Authenticate("Client Name here"))
return _invoker.Invoke(instance, inputs, out outputs);
else
{
outputs = null;
return null;
}
}
public object[] AllocateInputs()
{
return _invoker.AllocateInputs();
}
public IAsyncResult InvokeBegin(object instance, object[] inputs,
AsyncCallback callback, object state)
{
throw new NotSupportedException();
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
throw new NotSupportedException();
}
public bool IsSynchronous
{
get
{
return true;
}
}
#endregion
private bool Authenticate(string realm)
{
string[] credentials = GetCredentials(WebOperationContext.Current.IncomingRequest.Headers);
if (credentials != null && credentials.Length == 2)
{
// do auth here
var username = credentials[0];
var password = credentials[1];
// validate the username and password against whatever auth logic you have
return true; // if successful
}
WebOperationContext.Current.OutgoingResponse.Headers["WWW-Authenticate"] = string.Format("Basic realm=\"{0}\"", realm);
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
return false;
}
private string[] GetCredentials(WebHeaderCollection headers)
{
string credentials = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
if (credentials != null)
credentials = credentials.Trim();
if (!string.IsNullOrEmpty(credentials))
{
try
{
string[] credentialParts = credentials.Split(new[] {' '});
if (credentialParts.Length == 2 && credentialParts[0].Equals("basic", StringComparison.OrdinalIgnoreCase))
{
credentials = Encoding.ASCII.GetString(Convert.FromBase64String(credentialParts[1]));
credentialParts = credentials.Split(new[] {':'});
if (credentialParts.Length == 2)
return credentialParts;
}
}
catch
{
}
}
return null;
}
}

WCF REST - Overriding incoming request data

Is it possible to change the data of incoming HTTP request in WCF?
I've already found out how to change HTTP method (using IDispatchOperationSelector and HttpRequestMessageProperty of the incoming message).
I'm writing behavior that would enable to make "POST" requests using GET requests (with method and data stored in query string). I can override the HTTP method, but I can't find the solution to override the data. I need to load the data stored in query string and use them as HTTP body.
Any thoughts?
You'll need to recreate the incoming message so that the message body contains the information you want to pass. The body will likely be in either XML or JSON format (supported out-of-the-box). The code below shows one example of how this can be done.
public class StackOverflow_10391354
{
[ServiceContract]
public class Service
{
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public int Add(int x, int y)
{
return x + y;
}
}
class MyWebHttpBehavior : WebHttpBehavior
{
protected override WebHttpDispatchOperationSelector GetOperationSelector(ServiceEndpoint endpoint)
{
return new MyOperationSelector();
}
public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
base.ApplyDispatchBehavior(endpoint, endpointDispatcher);
}
}
class MyOperationSelector : WebHttpDispatchOperationSelector
{
protected override string SelectOperation(ref Message message, out bool uriMatched)
{
HttpRequestMessageProperty prop = (HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name];
if (message.Headers.To.LocalPath.EndsWith("/Add") && prop.Method == "GET")
{
prop.Method = "POST";
uriMatched = true;
message = CreateBodyMessage(message);
return "Add";
}
else
{
return base.SelectOperation(ref message, out uriMatched);
}
}
private Message CreateBodyMessage(Message message)
{
HttpRequestMessageProperty prop = message.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
string queryString = prop.QueryString;
NameValueCollection nvc = HttpUtility.ParseQueryString(queryString);
StringBuilder sb = new StringBuilder();
sb.Append('{');
bool first = true;
foreach (string key in nvc.Keys)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}
sb.Append('\"');
sb.Append(key);
sb.Append("\":\"");
sb.Append(nvc[key]);
sb.Append('\"');
}
sb.Append('}');
string json = sb.ToString();
XmlDictionaryReader jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(json), XmlDictionaryReaderQuotas.Max);
Message result = Message.CreateMessage(MessageVersion.None, null, jsonReader);
result.Properties.Add(HttpRequestMessageProperty.Name, prop);
result.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Json));
result.Headers.To = message.Headers.To;
return result;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new MyWebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
WebClient c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress + "/Add?x=66&y=88"));
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
You can use a message inspector: http://weblogs.asp.net/paolopia/archive/2007/08/23/writing-a-wcf-message-inspector.aspx

Categories