how to read query string values in WCF? - wcf

This may be very simple but i am not finding a way to read query string value in my WCF rest service. I tried the following but no joy
HttpContext.Current.Request.QueryString["name"]

Something like this should work for you. You need to use the UriTemplate. The following is the WCF service
[ServiceContract]
interface IPing
{
[OperationContract]
[WebInvoke(Method="POST", UriTemplate="stuff?n={name}&q={quantity}")]
void AddStuff(string name, string quantity, Stream data);
}
class PingService : IPing
{
public void AddStuff(string name, string quantity, Stream data)
{
Console.WriteLine("{0} : {1}", name, quantity);
Console.WriteLine("Data ...");
using (StreamReader sr = new StreamReader(data))
{
Console.WriteLine(sr.ReadToEnd());
}
}
}
And the client
static void Main(string[] args)
{
WebRequest req = WebRequest.Create("http://localhost:9000/ping/stuff?n=rich&q=20");
req.Method = "POST";
req.ContentType = "text/html";
using (StreamWriter sw = new StreamWriter(req.GetRequestStream()))
{
sw.WriteLine("Hello");
}
req.GetResponse();
}

Related

Serialize the Data into JSON

Would like to make this solution as a generic solution wherein instead of Person object, GetJSON Method should accept the generic type which is has the DataContract attribute. Can anybody explain how to go about doing it.
Here is the base code
namespace TestDataContractJsonSerializer
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Json;
public class Program
{
public static void Main(string[] args)
{
List<Person> persons = new List<Person> { new Person("Person1"), new Person("Person2") };
var strJSON = from p in persons select GetJSON(p);
Console.WriteLine("In JSON Format: ");
foreach (string str in strJSON)
{
Console.WriteLine(" {0}", str);
}
Console.ReadKey();
}
private static string GetJSON(Person p)
{
if (p != null)
{
MemoryStream stream = new MemoryStream();
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(Person));
dataContractJsonSerializer.WriteObject(stream, p);
stream.Position = 0;
StreamReader sr = new StreamReader(stream);
return sr.ReadToEnd();
}
return string.Empty;
}
}
[DataContract]
public class Person
{
public Person(string name)
{
this.Name = name;
}
[DataMember]
public string Name { get; private set; }
}
}
It would look something like this in the very basic case. You'd probably need to add some special casing and/or error handling, for instance if the type T is not serializable.
private static string GetJSON<T>(T objToSerialize)
{
if (objToSerialize != null)
{
MemoryStream stream = new MemoryStream();
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(objToSerialize.GetType());
dataContractJsonSerializer.WriteObject(stream, objToSerialize);
stream.Position = 0;
StreamReader sr = new StreamReader(stream);
return sr.ReadToEnd();
}
return string.Empty;
}

WCF REST Service: Method parameter (object) is null

why is the parameter of my WCF Rest service method always null?....I do access the service's method and i do get the string returned by the wcf method, but the parameter remains null.
Operation Contract:
[OperationContract]
[WebInvoke(UriTemplate = "AddNewLocation",
Method="POST",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
string AddNewLocation(NearByAttractions newLocation);
Implementation of AddNewLocation method
public string AddNewLocation(NearByAttractions newLocation)
{
if (newLocation == null)
{
//I'm always getting this text in my logfile
Log.Write("In add new location:- Is Null");
}
else
{
Log.Write("In add new location:- " );
}
//String is returned even though parameter is null
return "59";
}
Client code:
WebClient clientNewLocation = new WebClient();
clientNewLocation.Headers[HttpRequestHeader.ContentType] = "application/json";
JavaScriptSerializer js = new JavaScriptSerializer();
js.MaxJsonLength = Int32.MaxValue;
//Serialising location object to JSON
string serialLocation = js.Serialize(newLocation);
//uploading JSOn string and retrieve location's ID
string jsonLocationID = clientNewLocation.UploadString(GetURL() + "AddNewLocation", serialLocation);
I also tried this code in my client but still get a null parameter
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(NearByAttractions));
MemoryStream ms = new MemoryStream();
ser.WriteObject(ms, newLocation);
String json = Encoding.UTF8.GetString(ms.ToArray());
WebClient clientNewLocation = new WebClient();
clientNewLocation.Headers[HttpRequestHeader.ContentType] = "application/json";
string r = clientNewLocation.UploadString(GetURL() + "AddNewLocation", json);
Console.Write(r);
Then i also changed the BodyStyle option to "Bare" but then I got the following error (with both client codes):
The remote server returned an error: (400) Bad Request.
Any help please? thanks
Edit 1:
My GetUrl() method loads the web service IP address from the web config file and returns an object of type Uri
private static Uri GetURL()
{
Configuration config = WebConfigurationManager.OpenWebConfiguration("~/web.config");
string sURL = config.AppSettings.Settings["serviceURL"].Value;
Uri url = null;
try
{
url = new Uri(sURL);
}
catch (UriFormatException ufe)
{
Log.Write(ufe.Message);
}
catch (ArgumentNullException ane)
{
Log.Write(ane.Message);
}
catch (Exception ex)
{
Log.Write(ex.Message);
}
return url;
}
service address stored in web config as follows:
<appSettings>
<add key="serviceURL" value="http://192.168.2.123:55666/TTWebService.svc/"/>
</appSettings>
This is how my NearByAttraction class defined
[DataContractAttribute]
public class NearByAttractions
{
[DataMemberAttribute(Name = "ID")]
private int _ID;
public int ID
{
get { return _ID; }
set { _ID = value; }
}
[DataMemberAttribute(Name = "Latitude")]
private string _Latitude;
public string Latitude
{
get { return _Latitude; }
set { _Latitude = value; }
}
[DataMemberAttribute(Name = "Longitude")]
private string _Longitude;
public string Longitude
{
get { return _Longitude; }
set { _Longitude = value; }
}
You seem to be in the right track. You need the Bare body style, otherwise you'd need to wrap the serialized version of your input in another JSON object. The second code should work - but without more information about how the service is set up and what GetURL() returns we can only guess.
One way to find out what to send to a WCF REST service is to use a WCF client itself for that - using the WebChannelFactory<T> class, then use a tool such as Fiddler to see what it's sending. The example below is a SSCCE which shows your scenario working.
public class StackOverflow_15786448
{
[ServiceContract]
public interface ITest
{
[OperationContract]
[WebInvoke(UriTemplate = "AddNewLocation",
Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
string AddNewLocation(NearByAttractions newLocation);
}
public class NearByAttractions
{
public double Lat { get; set; }
public double Lng { get; set; }
public string Name { get; set; }
}
public class Service : ITest
{
public string AddNewLocation(NearByAttractions newLocation)
{
if (newLocation == null)
{
//I'm always getting this text in my logfile
Console.WriteLine("In add new location:- Is Null");
}
else
{
Console.WriteLine("In add new location:- ");
}
//String is returned even though parameter is null
return "59";
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
Console.WriteLine("Using WCF-based client (WebChannelFactory)");
var factory = new WebChannelFactory<ITest>(new Uri(baseAddress));
var proxy = factory.CreateChannel();
var newLocation = new NearByAttractions { Lat = 12, Lng = -34, Name = "56" };
Console.WriteLine(proxy.AddNewLocation(newLocation));
Console.WriteLine();
Console.WriteLine("Now with WebClient");
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(NearByAttractions));
MemoryStream ms = new MemoryStream();
ser.WriteObject(ms, newLocation);
String json = Encoding.UTF8.GetString(ms.ToArray());
WebClient clientNewLocation = new WebClient();
clientNewLocation.Headers[HttpRequestHeader.ContentType] = "application/json";
string r = clientNewLocation.UploadString(baseAddress + "/AddNewLocation", json);
Console.WriteLine(r);
}
}
Solved and thank you
I changed BodyStyle to "Bare" So my serivce interface is as follows:
[OperationContract]
[WebInvoke(UriTemplate = "AddNewLocation",
Method="POST",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
string AddNewLocation(NearByAttractions newLocation);
Then implemented my client as follows:
MemoryStream ms = new MemoryStream();
DataContractJsonSerializer serialToUpload = new DataContractJsonSerializer(typeof(NearByAttractions));
serialToUpload.WriteObject(ms, newLocation);
WebClient client = new WebClient();
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
client.UploadData(GetURL() + "AddNewLocation", "POST", ms.ToArray());
I used WebClient.UploadData instead of UploadString.

WCF Client reading JSONP responses

I'm writing a simple client for a web service using WCF. Unfortunately, the web service only answers with JSONP messages, not plain JSON.
Is that possible to use built-in features from .NET 4.0 to do this or do I need to extend something else to strip function name, { and } from the answer I get from the server? I know how to read JSON responses, but not JSONP yet.
What you need is a custom message encoder. On the server side, it's the encoder which adds the padding (function call) to the response, so you need something similar on the client side to remove that padding before handling the message (likely delegating it to another encoder). The other thing you'll need to worry about at the encoder is that often the content-type used for JSONP (application/x-javascript) is not recognized as a JSON content-type (because it's not, it's a function call), so the encoder should also "translate" that content-type into one which is understood by the encoder to which the call is delegated.
The code below shows an example of such an encoder. The service has been modified to always wrap the result, as you mentioned your service does.
public class StackOverflow_11255528
{
[ServiceContract]
public interface ICalculator
{
[WebGet(ResponseFormat = WebMessageFormat.Json)]
int Add(int x, int y);
[WebGet(ResponseFormat = WebMessageFormat.Json)]
int Subtract(int x, int y);
}
[ServiceContract]
public class CalculatorService
{
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public Stream Add(int x, int y)
{
return ReturnWrapped(x + y);
}
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public Stream Subtract(int x, int y)
{
return ReturnWrapped(x - y);
}
private Stream ReturnWrapped(int result)
{
string callback = "Something";
string response = string.Format("{0}({1});", callback, result);
WebOperationContext.Current.OutgoingResponse.ContentType = "application/x-javascript";
return new MemoryStream(Encoding.UTF8.GetBytes(response));
}
}
public class JsonpAwareClientMessageEncodingBindingElement : MessageEncodingBindingElement
{
WebMessageEncodingBindingElement webEncoding;
public JsonpAwareClientMessageEncodingBindingElement()
{
this.webEncoding = new WebMessageEncodingBindingElement();
}
public override MessageEncoderFactory CreateMessageEncoderFactory()
{
return new JsonpAwareClientMessageEncoderFactory(this.webEncoding.CreateMessageEncoderFactory());
}
public override MessageVersion MessageVersion
{
get { return this.webEncoding.MessageVersion; }
set { this.webEncoding.MessageVersion = value; }
}
public override BindingElement Clone()
{
return new JsonpAwareClientMessageEncodingBindingElement();
}
public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
{
context.BindingParameters.Add(this);
return context.BuildInnerChannelFactory<TChannel>();
}
class JsonpAwareClientMessageEncoderFactory : MessageEncoderFactory
{
private MessageEncoderFactory factory;
public JsonpAwareClientMessageEncoderFactory(MessageEncoderFactory factory)
{
this.factory = factory;
}
public override MessageEncoder Encoder
{
get { return new JsonpAwareClientMessageEncoder(this.factory.Encoder); }
}
public override MessageVersion MessageVersion
{
get { return this.factory.MessageVersion; }
}
}
class JsonpAwareClientMessageEncoder : MessageEncoder
{
private MessageEncoder encoder;
public JsonpAwareClientMessageEncoder(MessageEncoder encoder)
{
this.encoder = encoder;
}
public override string ContentType
{
get { return this.encoder.ContentType; }
}
public override string MediaType
{
get { return this.encoder.MediaType; }
}
public override MessageVersion MessageVersion
{
get { return this.encoder.MessageVersion; }
}
public override bool IsContentTypeSupported(string contentType)
{
if (contentType == "application/x-javascript")
{
contentType = "application/json";
}
return this.encoder.IsContentTypeSupported(contentType);
}
public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
{
if (contentType == "application/x-javascript")
{
contentType = "application/json";
}
byte openParenthesis = (byte)'(';
byte closeParenthesis = (byte)')';
int startOfParenthesis = buffer.Offset;
int count = buffer.Count;
while (buffer.Array[startOfParenthesis] != openParenthesis)
{
startOfParenthesis++;
count--;
}
// Skipped 'Func', now skipping '('
startOfParenthesis++;
count--;
// Now need to trim the closing parenthesis and semicolon, if any
int endOfParenthesis = buffer.Offset + buffer.Count - 1;
while (buffer.Array[endOfParenthesis] != closeParenthesis)
{
endOfParenthesis--;
count--;
}
// Skipped back to ')', now remove it
endOfParenthesis--;
count--;
return this.encoder.ReadMessage(new ArraySegment<byte>(buffer.Array, startOfParenthesis, count), bufferManager, contentType);
}
public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
{
throw new NotSupportedException("Streamed mode not supported");
}
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
return this.encoder.WriteMessage(message, maxMessageSize, bufferManager, messageOffset);
}
public override void WriteMessage(Message message, Stream stream)
{
throw new NotSupportedException("Streamed mode not supported");
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(CalculatorService), new Uri(baseAddress));
WebHttpBinding binding = new WebHttpBinding { CrossDomainScriptAccessEnabled = true };
host.AddServiceEndpoint(typeof(CalculatorService), binding, "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
WebClient c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress + "/Add?x=5&y=8&callback=Func"));
CustomBinding clientBinding = new CustomBinding(
new JsonpAwareClientMessageEncodingBindingElement(),
new HttpTransportBindingElement { ManualAddressing = true });
ChannelFactory<ICalculator> factory = new ChannelFactory<ICalculator>(clientBinding, new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
ICalculator proxy = factory.CreateChannel();
Console.WriteLine(proxy.Subtract(456, 432));
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
There is no direct way which I know but lets first try to understand difference between a JSON and JSONP
//JSON
{"prop":"val"}
//JSONP
func({"prop":"val"});
To get the JSON string you could simply strip every thing in between the "(" and ")" braces and then use different JSON libraries to convert it in to objects.
string jsonString = Regex.Match(jsonpString, #"\(([^)]*)\)").Groups[1].Value

problem with url when calling wcf service by httpWebRequest

When I call manually wcf service what should I type in url place :
HttpWebRequest httpWebRequest = WebRequest.Create(url)as HttpWebRequest;
should be there url to my svc file
http://localhost/service/LMTService.svc
or wsdl
http://localhost/service/LMTService.svc?wsdl
or url to service action ?
http://localhost/service/LMTService.svc/soap/GetSerializedSoapData
It depends on the binding of the endpoint. If using a SOAP binding (i.e., basicHttpBinding, wsHttpBinding, etc), the request URI should be the endpoint address (not the service address). Also, in some SOAP versions (such as SOAP11, used in basicHttpBinding), you need to specify the action as a HTTP header. If you're using webHttpBinding (with webHttp behavior) the address is the address of the endpoint, plus the UriTemplate (which by default is just the method name) of the operation you want to call.
The code below shows a HttpWebRequest-based request being sent to two endpoints, a one using BasicHttpBinding, one using WebHttpBinding.
public class StackOverflow_7525850
{
[ServiceContract]
public interface ITest
{
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
int Add(int x, int y);
}
public class Service : ITest
{
public int Add(int x, int y)
{
return x + y;
}
}
public static string SendRequest(string uri, string method, string contentType, string body, Dictionary<string, string> headers)
{
string responseBody = null;
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
req.Method = method;
if (headers != null)
{
foreach (string headerName in headers.Keys)
{
req.Headers[headerName] = headers[headerName];
}
}
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;
}
if (resp == null)
{
responseBody = 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)
{
responseBody = new StreamReader(respStream).ReadToEnd();
Console.WriteLine(responseBody);
}
else
{
Console.WriteLine("HttpWebResponse.GetResponseStream returned null");
}
}
Console.WriteLine();
Console.WriteLine(" *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* ");
Console.WriteLine();
return responseBody;
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "basic");
host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding(), "web").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
string soapBody = #"<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
<s:Body>
<Add xmlns=""http://tempuri.org/"">
<x>44</x>
<y>55</y>
</Add>
</s:Body>
</s:Envelope>";
SendRequest(baseAddress + "/basic", "POST", "text/xml", soapBody, new Dictionary<string, string> { { "SOAPAction", "http://tempuri.org/ITest/Add" } });
SendRequest(baseAddress + "/web/Add", "POST", "text/xml", "<Add xmlns=\"http://tempuri.org/\"><x>55</x><y>66</y></Add>", null);
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

call wcf service by HttpWebRequest

When I have that service:
[OperationContract]
ResponseMessage GetData(RequestMessage message);
Where
class RequestMessage
{
public string data
}
class ResponseMessage
{
public string data
}
and call this service
string data2 = ""
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost/Service.svc/GetData");
request.ContentType = "application/json";
request.Method = "POST";
request.KeepAlive = true;
using (Stream requestStream = request.GetRequestStream())
{
var bytes = Encoding.UTF8.GetBytes(data2);
requestStream.Write(bytes, 0, bytes.Length);
requestStream.Close();
}
var response = (HttpWebResponse)request.GetResponse();
var abc = new StreamReader(response.GetResponseStream()).ReadToEnd();
as data2 should I send string "mydata" or should I wrap it in json format : {"message": {"data":"mydata"}}
??
I have problem with understand how should be send data on client side by post to get it properly on service side :/
You didn't mention how the service is defined. Assuming your endpoint uses webHttpBinding, and an endpoint behavior with <webHttp/> with default values, then the default value for the body style is "Bare", which means that the request should contain only the serialized version of the parameter. For this case, you can send the string {"data":"hello world"}.
If you want a quick way to find what's the expected format for a WCF service, you can use a WCF client, using the same contract / binding / behaviors, and send a message to the server (and capture it on fiddler). For example, the code below shows a server similar to yours, and a client which sends a request to it.
public class StackOverflow_7492678
{
public class RequestMessage
{
public string data;
}
public class ResponseMessage
{
public string data;
}
[ServiceContract]
public interface ITest
{
[OperationContract]
ResponseMessage GetData(RequestMessage message);
}
public class Service : ITest
{
public ResponseMessage GetData(RequestMessage message)
{
return new ResponseMessage { data = message.data };
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
var endpoint = host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new WebHttpBinding(), new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.GetData(new RequestMessage { data = "mydata" }).data);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}