I have the following WCF REST web service interface :
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(
UriTemplate = "foobar/",
Method = "POST",
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare),
]
void PostFoobar(Foobar foobar);
}
The implementation :
public class Service : IService
{
public void PostFoobar(Foobar foobar)
{
try
{
Log.MonitoringLogger.Info("foo" + foobar.foo);
Log.MonitoringLogger.Info("bar" + foobar.bar);
}
catch (Exception ex)
{
if (Log.ExceptionLogger.IsErrorEnabled) Log.ExceptionLogger.Error(ex);
}
}
}
My Foobar class:
[DataContract]
public class Foobar
{
[DataMember]
public string foo { get; set; }
[DataMember]
public string bar { get; set; }
}
But it seems my Foobar object in parameter is always NULL when I call it from a client. I tried to implement the following method :
void PostFoobar(String foo, String bar);
And this time it works ! So my question is : Why, when I send a JSON Foobar object, it does not deserialized it ?
Here is a capture made with Wireshark to see if my client sent really the JSON object I expected :
So it seems the client send the object as expected : { "foo": "foo text", "bar": "bar text" }
I found something interesting: It works with the chrome plugin Simple REST client. I sent the json data:
{ "foo": "foo text", "bar" : "bar text" }
and it has worked.
So I have analysed the logs of my IIS server, and I found a difference : No element is catched from my client. The following object (in the logs)
<root type="object" xmlns="">
<foo type="string">foo chrome</foo>
<bar type="string">bar chrome</bar>
</root>
Appears only when I call my service from the chrome client.
Related
I'm trying to port an existing Soap WebService to .NET5 but am having issue with overloading a Soap method parameter.
In NET4 the code looks like this
namespace SoapWebServiceeTest.Soap
{
/// <summary>
/// Summary description for WsgSPServiceOrderService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class TextWebService : System.Web.Services.WebService
{
[WebMethod]
public string Test(
[XmlElement("object1", typeof(Object1))]
[XmlElement("object2", typeof(Object2))]
[XmlElement("object3", typeof(Object3))]
object request)
{
return $"{request.GetType().Name}";
}
}
public class Object1 { public string Param1 { get; set; } }
public class Object2 { public string Param2 { get; set; } }
public class Object3 { public string Param3 { get; set; } }
}
How do I achieve this in .NET5?
I have tried following but got reflection exception: System.Reflection.AmbiguousMatchException: 'Multiple custom attributes of the same type found.'
[ServiceContract]
public interface ITestWebService
{
[OperationContract]
string Test(
[XmlElement("object1", typeof(Object1))]
[XmlElement("object2", typeof(Object2))]
[XmlElement("object3", typeof(Object3))]
object request);
}
And also tried this but VS Add Connected Services errored with "More than one message named 'ISampleService_Test_InputMessage' was specified. Each message must have a unique name."
[OperationContract]
string Test(Object1 request);
[OperationContract]
string Test(Object2 request);
Any help would be awesome
You may try this
You can make it post or get based on your need
[OperationContract]
[WebInvoke(UriTemplate = "Test1", Method = "POST"]
string Test(Object1 request);
and
[OperationContract]
[WebInvoke(UriTemplate = "Test2", Method = "POST"]
string Test(Object2 request);
This way you can achieve objective
I'm trying to create a WCF SOAP service that has a service method that accepts bare parameters in the body but I just cant make it happen. At the moment, the method name element is being created under the body. I'm trying to use ws-addressing so that the method name is part of the header and the parameters are the direct children of the body.
Here's my service implementation:
[SoapDocumentService(Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Bare)]
public class Service1 : IService1
{
[SoapDocumentMethod(Use=SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Bare)]
public void DoWork([XmlElement(Namespace = "http://www.contoso.com",
IsNullable = true)] MyClass wrapper)
{
}
}
[XmlRoot(Namespace = "http://www.contoso.com")]
public class MyClass
{
public int Value { get; set; }
public string MyProperty { get; set; }
}
[ServiceContract]
public interface IService1
{
[OperationContract]
void DoWork(MyClass wrapper);
}
The above implementation generates the soap client below. But I'm trying to have the wrapper element as the direct child on body (trying to remove DoWork). From what I've read, decorating the svc method to use bare parameters should remove the service method name (DoWork) and use ws-addressing.
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:web="http://schemas.datacontract.org/2004/07/WebApplication2">
<soap:Header/>
<soap:Body>
<tem:DoWork> <!-- I want to remove this svc method name element -- >
<tem:wrapper> <!-- I want this to be under the body -->
<!--Optional:-->
<web:MyProperty>?</web:MyProperty>
<!--Optional:-->
<web:Value>?</web:Value>
</tem:wrapper>
</tem:DoWork>
</soap:Body>
</soap:Envelope>
I've followed the guide from msdn to decorate the service method. MSDN Link
I think you should drop the wrapper. in .net 2 this would work, wcf should be similar:
[WebMethod]
[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare)]
public String EchoString(String s, String s1)
{
return s;
}
I had to create message contracts wrapper for the MyClass and specify the message body.
[MessageContract]
public class MyWrapper
{
[MessageBodyMember]
public int Value { get; set; }
[MessageBodyMember]
public string MyProperty { get; set; }
}
I have a restful service based on WCF like below:
(The FeedbackInfo class has only one enum member - ServiceCode.)
[OperationContract]
[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public List<FeedbackInfo> GetFeedbackInfoList(ServiceCode code)
{
return ALLDAO.GetFeedbackInfoList(code);
}
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public int? CreateFeedback(FeedbackInfo feedback)
{
return ALLDAO.CreateFeedback(feedback);
}
I will use jquery ajax to invoke these two method like below:
$.ajax({
type: "GET",
url: "/Service/ALL.svc/GetFeedbackInfoList",
datatype: "text/json",
data: { "code": "Info"},
});
var feedbackInfo = { feedback: {
ServiceCode: "Info"
}};
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Service/ALL.svc/CreateFeedback",
datatype: "text/json",
data: JSON.stringify(feedbackInfo),
});
The first calling will be excuted succesfully, whereas the second one give me an error: The value "Info" cannot be parsed as the type 'Int64'. I'm wondering why the same one enum can not be parsed in the second calling? Just because of the enum type being used as a member of class?
EDIT:
The FeedbackInfo and ServiceCode are looked like below:
public class FeedbackInfo
{
public int ID { get; set; }
public string Title { get; set; }
public ServiceCode ServiceCode { get; set; }
}
[DataContract]
public enum ServiceCode
{
[EnumMember]
Info,
[EnumMember]
Error
}
I have put together a better solution that uses the Newtonsoft.Json library. It fixes the enum issue and also makes the error handling much better. It's quite a lot of code, so you can find it on GitHub here: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs
You have to add some entries to your Web.config to get it to work, you can see an example file here:
https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config
Enums are serialized as integers, so you'd need to use ServiceCode: 1 (or whatever), or alternatively, add a custom property in the FeedbackInfo class to deserialize the enum from a given string value. Ie, something like this:
public string ServiceCode {
get {
return ServiceCodeEnum.ToString();
}
set {
MyEnum enumVal;
if (Enum.TryParse<MyEnum>(value, out enumVal))
ServiceCodeEnum = enumVal;
else
ServiceCodeEnum = default(MyEnum);
}
}
private MyEnum ServiceCodeEnum { get; set; }
I have written a WCf Service which has a Collection type input body parameter and another parameter as query string as following:
[WebInvoke(Method = "PUT", UriTemplate = "users/role/{userID}",BodyStyle=WebMessageBodyStyle.WrappedRequest)]
[OperationContract]
public bool AssignUserRole(int userID,Collection<int> roleIDs)
{
//do something
return restult;
}
Now when I am trying to pass this parameter I am getting de serializing error. I have tried following format:
<AssignUserRole xmlns="http://tempuri.org/">
<roleIDs>
<roleID>7</roleID>
</roleIDs>
</AssignUserRole>
<AssignUserRole xmlns="http://tempuri.org/">
<ArrayOfroleID>
<roleID>7</roleID>
</ArrayOfroleID>
</AssignUserRole>
<AssignUserRole xmlns="http://tempuri.org/">
<ArrayOfint>
<int>7</int>
</ArrayOfint>
</AssignUserRole>
Can some one help me how can I pass this Array(Collection type Body parameter)?
Thanks.
The correct format would be this:
<AssignUserRole xmlns="http://tempuri.org/">
<roleIDs xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:int>7</a:int>
<a:int>8</a:int>
</roleIDs>
</AssignUserRole>
One easy way to find out what the expected format is for a certain operation is to use a WCF client with the same contract, send a message with it and look at the operation using Fiddler. The program below does that.
public class StackOverflow_6339286
{
[ServiceContract]
public interface ITest
{
[WebInvoke(Method = "PUT", UriTemplate = "users/role/{userID}", BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[OperationContract]
bool AssignUserRole(string userID, Collection<int> roleIDs);
}
public class Service : ITest
{
public bool AssignUserRole(string userID, Collection<int> roleIDs)
{
return true;
}
}
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");
WebChannelFactory<ITest> factory = new WebChannelFactory<ITest>(new Uri(baseAddress));
ITest proxy = factory.CreateChannel();
proxy.AssignUserRole("1234", new Collection<int> { 1, 2, 3, 4 });
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
Also notice that there's a problem in your UriTemplate: the path variable {userId} cannot be of type int (it must be a string). This is fixed in the sample code above.
One more thing: if you don't want to use the default namespace for collections / arrays, you can use a [CollectionDataContract] class to change it. If instead of using Collection you used the class below, then the first body you tried should work:
[CollectionDataContract(Namespace = "http://tempuri.org/", ItemName = "roleID")]
public class MyCollection : Collection<int> { }
I have written a really simple wcf rest service which seems to work fine when I make requests to it through fiddler but I cannot get it to work when calling it from JQuery.
Service:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "customers/{regionId}",
ResponseFormat = WebMessageFormat.Json
)]
Customer[] GetCustomers(String regionId);
}
[DataContract]
public class Customer
{
[DataMember]
public Guid Id { get; private set; }
[DataMember]
public String Name { get; private set; }
public Customer(Guid id, String name)
{
Id = id;
Name = name;
}
}
public class Service1 : IService1
{
public Customer[] GetCustomers(String regionId)
{
return new[]
{
new Customer(Guid.NewGuid(), "john"),
new Customer(Guid.NewGuid(), "pete"),
new Customer(Guid.NewGuid(), "ralph")
};
}
}
I can make requests to this service via fiddler and it returns the expected json. However, when I try and call it with JQuery ajax via the firebug console it always fails. Here is the call:
$.ajax({
type: "POST",
data: "{}",
url: "http://127.0.0.1:8081/json/customers/1",
contentType: "application/json; charset=utf-8",
dataType: "json",
success:function(res)
{
alert('success');
},
error: function(xhr)
{
alert('failed: '+xhr.responseText);
}
});
I always get the failed alert and the responseText is always blank. Any ideas would be greatly appreciated.
When you say "via Fiddler" do you mean "using Fiddler's request builder" or do you mean "with Fiddler running?"
Question: What URL is your website running on? You cannot generally make XHR requests to different servers (or ports, in FF) using XHR.