I have a WCF endpoint that is like such:
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json, UriTemplate = "")]
Stream DoWork(Dictionary<string, string> items);
In order to pass anything to my service, I have to structure my JSON like such:
{"items":[{"Key":"random1","Value":"value1"}, {"Key":"random2","Value":"value2"}]}
What I actually want it to look like is this:
{"items":{"random1":"value1","random2":"value2"}}
Is there any way to accomplish this?
Is it an option for you to change the DoWork parameter to a string, then use a Json deserializer in the method to convert it to the appropriate format?
I have been searching for the same solution. I managed to get it working by using 'JavaScriptSerializer'. You have to set the function output to 'Stream' not 'String'.
Public Function hotel_availability(ByVal data1 As Stream) As Stream Implements IMyTestAPI.hotel_availability
....
Dim serializer As New JavaScriptSerializer()
Dim serializedResult = serializer.Serialize(a_response)
Dim json = Encoding.UTF8.GetBytes(serializedResult)
Dim a_result as New MemoryStream(json)
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8"
return a_result
You basically need a SerializableDynamic Object, so that your method will look like this:
[OperationContract]
[WebInvoke(...)]
Stream DoWork(SerializableDynamicObject items);
You can see a good guide on how to build the SerializableDynamic Object from a Dictionary here: (see Solution section). Hope this helps...
You may have better success using the Newtonsoft JSON serializer.
It is available here http://www.newtonsoft.com/json for free and is also available as a NuGet package.
I have found to be much more flexible than the stock JSON serializers.
Also, it looks like your URITemplate is empty. I haven't used the wrapped body style, but with bare body style you need the URITemplate to be populated.
Related
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!
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.
I am sending a JSON message including filename, and a base64encoded image to a WCF service. I am not really sure on how to convert it back to the image, more specificallly deserializing the return stream.
WCF Interface
[OperationContract]
[WebInvoke(
Method = "POST",
UriTemplate = "/UploadImage", ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
string UploadImage(Stream image);
And some part of the message(which I save to a file so I can view and try to understand)
--hr56lXG6Q_hKg5opmTx4xejr28dU17AC
Content-Disposition: form-data; name="entity"
{"filename":"mypicture.jpg","thebigfile":"\/9j\/4Re6RXhpZgAATU0AKgAAAAgACwEPAAIAAAAOAAAAkgEQAAIAAAAGAAAAoAESAAMAAAABAAYAAAEaAAUAAAABAAAApgEbAAUAAAABAAAArgEoAAMAAAABAAIAAAExAAIAAAATAAAAtgEyAAIAAAAUAAAAygITAAMAAAABAAEAAIdpAAQAAAABAAAA3oglAAQAAAABAAACegAAAoBTb255IEVyaWNzc29uAExUMjZpAAAAAEgAAAABAAAASAAAAAE2LjEuQS4yLjQ1XzUzX2YxMDAApDIwMTI6MTA6MDYgMDk6MzI6MTcAABiCmgAF
and lots more of the base64 encoded image....
--hr56lXG6Q_hKg5opmTx4xejr28dU17AC--
How do I deserialize this? Is Stream the way to go? I do not simply want to remove the top rows and then start deserializing the JSON array, I want to know WHY it looks like this.
To process the message you gave, the OperationContract needs to look something like:
[WebInvoke(Method="POST", UriTemplate="/UploadImage", BodyStyle=WebMessageBodyStyle.WrappedRequest, ResponseFormat=WebMessageFormat.Json, RequestFormat=WebMessageFormat.Json)]
[OperationContract]
string UploadImage(string filename, string thebigfile);
See http://msdn.microsoft.com/en-us/library/bb885100.aspx for more information
You would then need to manually Base64-decode the "thebigfile" parameter using the decoder provided by the .NET Framework. There is no built-in support for Base64 inside JSON as far as I know, see http://msdn.microsoft.com/en-us/library/bb412170.aspx for details on how various data types are supported.
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.
i am using the Plain old XML template from WCF REST Starter Kit Preview 2 to build a REST service and i would like to set the ResponseFormat inside the OperationContract, not outside(not with ResponseFormat = WebMessageFormat.Json) because i want to set it accordingly to the Accept Header in the Request. and ideally i would like to be able to set the RequestFormat from inside the OperationContract also, acording to the Content-Type header.
So, could anyone tell me how i can do it, or point me towards some examples.
chers,
Later Edit: so i don't want to have things like "format" in the UriTemplate, i just want one UriTemplate, no ResponseFormat (and no RequestFormat at some point) and the ResponseBody to be output according to the AcceptHeader
[WebHelp(Comment = "For Service testing purposes")]
[WebGet(UriTemplate = "Echo")]
[OperationContract]
public ResponseBody Echo()
You'll have to strip off most of the WCF REST code and rebuild it yourself. There is no support for content-type negotiation out of the box. I am lead to believe the starter kit has an example using their extensibility points, you may want to check with them.
http://damianblog.com/2008/10/31/wcf-rest-dynamic-response/ is a good place to start