I have one WCF service and one single method named GetStudentList() in the service.It's working fine when it's return single response.Something like this
[WebGet(ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
List<Student> GetStudentList();
But i want to return multiple response i.e. xml and json both.something like this
[WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[WebGet(ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
List<Student> GetStudentList();
Is it possible?if yes then how?
It is possible in .NET 4.0 but not in the way you specified it. .NET 4.0 adds new parameter to WebHttp behavior:
<endpointBehaviors>
<behavior name="WebHttp">
<webHttp automaticFormatSelectionEnabled="true" />
</behavior>
</endpointBehaviors>
When you use automatic format selection the format of the response is based on:
Accept header from the request
Content type of the request
Default format specified on operation
Default format specified in webHttp behavior
So if you call your REST service with JSON request you will get JSON. If you call it with POX request you will get XML. Full description of automatic format selection is in MSDN.
I do not think it is possible to return the object as both Json and XML using one call. Think of WCF like a normal method call in this regard; you call one method, you get one serialized return value. Once the service has returned one response to the caller, the call is complete.
Think carefully about why you want to use both response types; they are both informative, universal standards for object serialization, and using WCF, you would only need both if you were using the serialized response text directly. If at all possible, I would refactor the clients to work with the same response type.
The simplest workaround, if two types really are needed, would be to provide two "overloads" of this method, and make each client type smart enough to know which call it needs to make. Because the difference is not in the method signature, it's not a true overload; you'll have to separate them either by name (GetStudentListJSON vs GetStudentListXML) or by locating the methods in different service classes.
You could also always return one response type, and convert on the client side by deserializing/reserializing when you need the object serialized in the other format. This does require you to be using .NET code that you have control over on the client side of the call.
I don't know of a way where you can get 2 outputs from a service operation. You can always get the XML (serialized DataContract) and then "JSON Serialize" it. Here is how you can do it.
List<Student> studentList = GetStudent();
string jsonString = JsonSerialize(studentList.GetType(), studentList);
And then add this function to a utility class:
public static string JsonSerialize(Type type, object objectGraph)
{
MemoryStream memoryStream = new MemoryStream();
try
{
System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(type);
serializer.WriteObject(memoryStream, objectGraph);
return Encoding.Default.GetString(memoryStream.ToArray());
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (memoryStream != null) memoryStream.Close();
}
}
Related
We have a WCF Rest Json service hosted on the server and it consume from the native iPad application. Most of the users , it works fine while calling WCF service. But for some users, we found that WCF receives the currupted or clipped Json string. Some part of the Json string is clipped and it failed to deserialize from the service end.
For the account creation issue , we have added the logging of the service exception in the webservice to the notepad file. We got the exceptions from the notepad file. Right now we could see the parameters to the services is missing for those users.and it cause the error while deserializing the parameter. We need to check the app side why the the parameters are coming as empty. Right now we got the same user account creation exceptionentries from the "50.143.85.75" IP.
We have WCF Rest service CreateUser"as like below. We are consuming this service from the IOS app.
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Json,
UriTemplate = "CreateUser/")]
[OperationContract]
CustomServiceResponse CreateUser(Stream userdetails);
Method Implementation code is like below.
public CustomServiceResponse CreateUser(Stream user)
{
var stream = new StreamReader(user);
string streamcontent = stream.ReadToEnd();
UserInformation userInformation = ConvertJSonToObject<UserInformation>(streamcontent);
}
Following are the biding configuration
<binding name="webHttpBindingStreamed"
transferMode="Streamed"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647"
openTimeout="00:25:00"
closeTimeout="00:25:00"
sendTimeout="00:25:00"
receiveTimeout="00:25:00"></binding>
</webHttpBinding>
Right now the service converting the stream to string and doing all operations. But for sometime we are getting error.
We have logged the streamcontent when the deserialization failure. we could see that serialization failing when the streamcontent is not containg full json string. below you can see the json string we got while we got the exception. you can see the double quotes and closing bracket missing from the first json string and some letter ("false") and closing bracket skipped from second json String.
{"FirstName":"Donald","LastCBPaymentType":6,"AcceptEmail":true,"CellPhone":"111-111-6874","Position":"3","UserAccountDateTime":"\/Date(1457055496621+0800)\/","CBExpirationDate":"\/Date(1465862400000+0800)\/","Email":"test.test#gmail.com","Password":"test","IsNeedtoGiveFreeOneMonth":false,"LastName":"test","WBExpirationDate":null,"IpAddress":"72.188.215.34","CarrierNum":"4","EmpNum":"96874
{"Position":"4","Email":"Tim.rrrr#wnco.com","CellPhone":"111-11-6146","LastCBPaymentType":6,"EmpNum":"107333","FirstName":"Timothy","Password":"eeee","WBExpirationDate":null,"UserAccountDateTime":"\/Date(1457054214674+0800)\/","CarrierNum":"5","CBExpirationDate":"\/Date(1468281600000+0800)\/","AcceptEmail":true,"IpAddress":"73.209.67.242","LastName":"Hill","IsNeedtoGiveFreeOneMonth":fa
Do you know why this some portion of the json string clipped ? Is there any serialization issue from the IOS side?
I am building a couple of REST web services with WCF. I have some POST services to which I pass a list of objects in the wrapped HTTP body like this:
[OperationContract]
[WebInvoke(
Method = "POST",
UriTemplate = "DoSomething/pages?term={term}",
BodyStyle = WebMessageBodyStyle.Wrapped)
]
MyResponseContainer DoSomethingFromPages(string term, List<Page> pages);
This works just fine. I can wrap the List<Page> into JSON or XML and put it into the body and it is deserialized automatically.
In a similar way I have another service where I read a binary stream (a file) from the HTTP body like this:
[OperationContract]
[WebInvoke(
Method = "POST",
UriTemplate = "DoSomething/upload?term={term}",
BodyStyle = WebMessageBodyStyle.Bare)
]
MyResponseContainer DoSomethingUploadFile(Stream httpBody, string term);
Now my question is: Can I somehow combine both ways? So basically I want to pass a list of custom objects together with a (file)stream. And I would like to avoid using multi-part forms style.
Thanks a lot!
I have self-hosted WCF service. It works as charm, when the input data format is known, but it's not my case.
My service would be invoked like a HTML form would be posted to it, but I don't know exact list of parameters which would be sent.
External web service would send a HTTP request with content "a=foo&b=bar", yes, URL-encoded. Parameters in content may vary. Their count may vary. Some parameters may or may not be present. So I'm perfectly fine with whole URL-encoded string, I'll decode it myself. I have to reply with ASCII string "OK".
How to access this "a=foo&b=bar" text from the request? Can I create my own encoder? How it's done?
Here's what I started with, the contract:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "test", BodyStyle = WebMessageBodyStyle.Bare)]
Stream TestPostRequest();
And here's the method:
public Stream TestPostRequest() {
var maxByteSize = 16384;
var messageStream = new MemoryStream(maxByteSize);
var messageBuffer = OperationContext.Current.RequestContext.RequestMessage.CreateBufferedCopy(maxByteSize);
messageBuffer.WriteMessage(messageStream);
var rawRequestData = new byte[maxByteSize];
messageStream.Read(rawRequestData, 0, maxByteSize);
var requestDataString = Encoding.UTF8.GetString(rawRequestData);
Console.WriteLine(requestDataString);
var responseData = new byte[2] { 79, 75 }; // OK
var responseStream = new MemoryStream(responseData);
return responseStream;
}
Obviously it doesn't work, because CreateBufferedCopy method expects XML as input data, but gets URL-encoded string. As all the other methods I found.
I'm not afraid of creating new class implementing IEndpointBehavior or IDispatchMessageInspector, but I just don't see where I could implement something allowing me to access the request data raw, as bytes, not XML.
Maybe there is a point where I could convert raw data to XML?
EDIT:
My fault, there was a broken behavior in my operation stack which messed with message content type badly. The moral is - test new features isolated, in a clean test project. The second moral is WCF can everything ;) And the third is: if there is no XML in message internal data - you broke it ;)
Since updating our project to Visual Studio 2012 and a targeting the .NET framework 4.5 from 4.0, we've been intermittently experiencing serialization exception issues when attempting to return data from our service methods, though for purposes of debugging the problem, we've focused our attention on a single service method, GetPageSelectListForSite.
This methods interface declaration is the following:
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
UriTemplate = "/Pages/Site/ListItems/{siteId}",
BodyStyle = WebMessageBodyStyle.Bare),
Description("")]
IEnumerable<PageInfoItem> GetPreviousPages(string siteId);
It is called with the same signature parameter, and the same IEnumerable data is returned inside of a controller; however, occassionally (once every 100 or so times) this will result in a serialization exception when the controller is trying to deserialize the REST service reply:
System.Runtime.Serialization.SerializationException: There was an error deserializing the object of type System.Collections.Generic.IEnumerable`1[[Synodine.Hkd.Models.PageInfoItem, Synodine.Hkd.Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]. Encountered unexpected character 'D'. ---> System.Xml.XmlException: Encountered unexpected character 'D'.
The full exception and corresponding stack trace is available here:
http://pastebin.com/jaqQDbru
PageInfoItem is a fairly simple class containing a string, and derives from ListInfoBase which contains a guid, some strings, some bools, and a DateTime.
Attempts to decorate the classes with [Serializable] AND/OR [DataContract]/[DataMember] have not had any effect. We have not been able to consistently reproduce the problem.
I should note that we've seen the serialization exception error occur on an extremely simple REST GET method, with response/request formats set to Json and BodyStyle of Bare that returns a single string. It doesn't seem to matter if the rest service method in question is a POST/GET or whether its style is set to Bare or Wrapped, though in all cases we are making use of the WebMessageFormat.Json.
We've tried serializing the data ourselves and logging it to see if the JSON was malformed in some way, but the JSON is valid and is identical across failed/succeeded requests.
This was the code we used to serialize the data:
var stream1 = new MemoryStream();
var ser = new DataContractJsonSerializer(typeof(IEnumerable<PageInfoItem>));
ser.WriteObject(stream1, PageInfoItems);
stream1.Position = 0;
var streamReader = new StreamReader(stream1);
A sample of the JSON that is being logged is:
[
{
"DateLastUpdate": "\/Date(1362438368000+0000)\/",
"DateLastUpdateString": null,
"Id": "df2544e6-71a3-4f1c-ac54-d3c85269804f",
"IsSelected": false,
"Name": "samplepage",
"Path": "\/samplepage"
},
<snip>
]
This problem only occurs in our deployed environments. All attempts to reproduce the problem on our local IIS webservers have failed.
We are using the WebChannelFactory to consume the WCF REST Services. Because of this, we don't have (and are not sure how to get) direct access to the Response so we cannot be 100% of the data being sent across the wire.
Has anyone encountered anything similar to this when upgrading their projects to Visual Studio 2012/.NET 4.5 Framework?
Any responses definitely appreciated,
-- Shaun
Can you try exposing another function which return same data that GetPreviousPage returns, but changing the ResponseFormat to say Xml? Then call it multiple times (e.g. from a browser) and see if this new function ever fails. This well help you know if the problem is with Json Serializer.
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Json,
UriTemplate = "/Pages/Site/ListItemsNew/{siteId}",
BodyStyle = WebMessageBodyStyle.Bare),
Description("")]
IEnumerable<PageInfoItem> GetPreviousPagesNew(string siteId);
PS: Posted this as an answer as it's difficult to post code in comment.
Is there a way to dynamically change the WebResponseFormat on a method given a parameter passed by the client? I default my WebResponseFormat to XML, but I want to give the client the opportunity to specify a format as JSON or XML and if none is specified, default to XML.
Currently I am doing the following:
[WebGet(UriTemplate = "objects", BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract]
List<SampleObject> GetObjects();
The user can call it via:
http://localhost/rest/myservice/objects
They then can specify a format by doing:
http://localhost/rest/myservice/objects?format=json
The problem is that when I try to set the response content type via:
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
That just returns the XML but the browser attempts to process it like a JSON object instead of serializing the response as JSON.
Is this even possible with .NET 3.5 outside of using a Stream as the return value and serializing the response myself? If not, is there a better solution?
I was able to resolve this by doing the following:
[WebGet(UriTemplate = "objects", BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract]
List<SampleObject> GetObjects();
[WebGet(UriTemplate = "objects?format=json", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
List<SampleObject> GetObjectsInJson();
It isn't pretty, but if format=xml is defined or left off, it will default to my operation contract, but if format=json is defined, it defaults to the second operation contract. This successfully returns the result as JSON and XML as desired.
For .NET 3.5 and WCF REST 3.5, I don't know of any way to do this elegantly.
.NET 4 and WCF REST in .NET 4 will support the "content negotiation" strategy that you use - just setting the ContentType = "application/json" will cause the service to automagically return JSON instead of XML.
So if there's any chance for you, wait for .NET 4 (should be out mid April 2010) and use that - it should offer lots of improvements in WCF anyway, especially in the WCF REST area.