I am using WCF .NET 4.0 hosting inside WebServiceHost. Normaly all works until i use my own defined class array inside class.
Server side function
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "foo")]
[OperationContract]
void Foo(FooQuery query);
Classes
[DataContract(Namespace="")]
public class FooQuery
{
[DataMember]
public MyFoo[] FooArray;
}
[DataContract(Namespace = "")]
public class MyFoo
{
[DataMember]
public string[] items;
}
Client side:
//create object
FooQuery myOriginalFoo = new FooQuery();
MyFoo _myFoo = new MyFoo();
_myFoo.items = new string[] { "one", "two" };
myOriginalFoo.FooArray = new MyFoo[] { _myFoo };
//serialize
var json = new JavaScriptSerializer().Serialize(myOriginalFoo);
string _text = json.ToString();
//output:
// {"FooArray":[{"items":["one","two"]}]}
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://localhost:2213/foo");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
streamWriter.Write(_text);
streamWriter.Flush();
streamWriter.Close();
}
//here server give back: 400 Bad Request.
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
I have also tried manipulate my class with System.Runtime.Serialization.Json.DataContractJsonSerializer - all fine until i send to server and webinvoke give back error 400. Why webInvoke don't know how deserialize it or there any other error?
I found magic attribute called CollectionDataContract what makes a trick.
Adding new collection class:
[CollectionDataContract(Namespace = "")]
public class MyFooCollection : List<MyFoo>
{
}
Changed Query class
[DataContract(Namespace="")]
public class FooQuery
{
[DataMember]
public /*MyFoo[]*/MyFooCollection FooArray;
}
client code change:
MyFooCollection _collection = new MyFooCollection();
_collection.Add(_myFoo);
myOriginalFoo.FooArray = _collection; //new MyFoo[] { _myFoo };
All variables serialized now right :) yeah..takes many hours to figure out.
Since it is a web request, try GET instead:
[WebGet(ResponseFormat = WebMessageFormat.Json)]
Set the WebMessageBodyStyle to wrappedRequest as below for the WCF service to expect a wrapped JSON string. by default it expects a plain String.
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
Related
I am configuring a WCF client service programmatically, this is because we are executing libraries in a third-party sandbox where we have no control of the AppDomains configuration file.
The problem is that the standard 'public static void Configure(ServiceConfiguration config)' isn't being called automatically, this results in the client not being configured.
Here's the client with the static Configure method:
namespace OpenIt.service
{
public class CustomerClient : ClientBase<ICustomerClient>, ICustomerClient
{
public static void Configure(ServiceConfiguration config)
{
WebHttpBinding binding = new WebHttpBinding
{
TransferMode = TransferMode.Streamed
};
var serviceEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(ICustomerClient)), binding, new EndpointAddress(new Uri("http://localhost:3245")));
serviceEndpoint.Behaviors.Add(new WebHttpBehavior
{
DefaultOutgoingRequestFormat = System.ServiceModel.Web.WebMessageFormat.Json
});
config.AddServiceEndpoint(serviceEndpoint);
config.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true });
}
public GetCustomerResponse GetCustomer(GetCustomerRequest request)
{
using (new OperationContextScope((IContextChannel)Channel))
{
GetCustomerResponse response = Channel.GetCustomer(request);
return response;
}
}
}
}
Here's the interface:
namespace OpenI
{
[ServiceContract]
public interface ICustomerClient
{
[OperationContract]
[WebInvoke(
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
Method = "POST",
UriTemplate = "rest/v6/customerMan/customer/GetCustomer")]
GetCustomerResponse GetCustomer(GetCustomerRequest request);
}
}
Here's the initialization and call:
private bool Testit()
{
//Here it fails due to no config
var client = new CustomerClient();
GetCustomerResponse response = client.GetCustomer(new GetCustomerRequest { CustomerNumber = "001" });
}
Does anyone have any idea why the 'public static void Configure(ServiceConfiguration config)' method isn't automatically being called? Am I missing something where 'CustomerClient()' is initialized under 'Testit()'.
The webservice is fine, tested with postman.
This method is used to configure WCF services, not clients.
Strong hint is that the parameter is of type ServiceConfiguration.
See the documentation for details.
To configure a client, you can use the methods (and constructors) on ClientBase and/or app.config settings (docs).
i have created WCF Service with POST request.
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
// BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "/UpdateRCFOnline"
)]
String UpdateRCFOnline(RCFOnline rcf_class);
public string UpdateRCFOnline(RCFOnline rcf_class)
{
string success = string.empty;
try
{
//POST data to DB
success = "Update Success";
} catch(Exception ex){
success = "Update Failed, " +ex.message;
}
return success;
}
How to make this POST request returning "Success" as JSON. Cause if i tried this service in Fiddler. I got message on "Raw" tab not JSON.
Try below
public object UpdateRCFOnline(RCFOnline rcf_class)
{
try
{
//POST data to DB
return new {
success = "Update Success";
}
} catch(Exception ex){
return new {
success = "Update Failed, " +ex.message;
}
}
}
This way you are returning an object which will be deserialised to json string.
Note. also check if you can set return data type to json
I create a wcf rest service .as you can see the details of that :
[ServiceContract]
public interface INewsRepository
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/AddNews", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
bool Add(News entity);
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/DeleteNews/{id}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
bool Remove(string id);
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/EditNews", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
bool Edit(News entity);
}
I call this service in my client like this :
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Web;
using System.Web.Script.Serialization;
using CMSManagement.Domain.Entity;
using UPTO.UI.Infrastructure.Configuration;
using System.Text;
namespace UPTO.UI.Services
{
public class NewsRepository : IServiceRepository<News>
{
private WebClient ClientRequest;
private MemoryStream ms;
private DataContractJsonSerializer serializerToUplaod;
public string ServiceHostName = "http://cmsmanagement"+ ServiceServer.Address;
public NewsRepository()
{
ClientRequest = new WebClient();
ClientRequest.Headers["Content-type"] = "application/json";
ms= new MemoryStream();
}
public bool Add(News data)
{
serializerToUplaod = new DataContractJsonSerializer(typeof(News));
serializerToUplaod.WriteObject(ms, data);
string Result = System.Text.Encoding.UTF8.GetString(ClientRequest.UploadData(ServiceHostName+"/NewsRepository.svc/AddNews", "POST", ms.ToArray()));
return Result.ToLower().Equals("true");
}
public bool Delete(string id)
{
byte[] data = ClientRequest.UploadData(ServiceHostName+"/NewsRepository.svc/DeleteNews/"+id, "POST", ms.ToArray());
string Result = System.Text.Encoding.UTF8.GetString(data);
return Result.ToLower().Equals("true");
}
public bool Edit(News data)
{
serializerToUplaod = new DataContractJsonSerializer(typeof(News));
serializerToUplaod.WriteObject(ms, data);
string Result = System.Text.Encoding.UTF8.GetString(ClientRequest.UploadData("http://localhost:47026/NewsRepository.svc/EditNews", "POST", ms.ToArray()));
return Result.ToLower().Equals("true");
}
}
}
The problem is the edit part of my service :
When i call my edit method i get this error in my wcf log :
The incoming message has an unexpected message format 'Raw'. The expected message formats for the operation are 'Xml', 'Json'. This can be because a WebContentTypeMapper has not been configured on the binding. See the documentation of WebContentTypeMapper for more details.
Finally i found that i should move
ClientRequest.Headers["Content-type"] = "application/json";
from constructor to my Edit method .Why ?
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).
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.