Is there way to change the wcf contract from.
This
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://schemas.datacontract.org/2004/07/">
<soapenv:Header/>
<soapenv:Body>
**<AddCustomer>**
<!--Optional:-->
<Data>
<ns:AddressLine1>?</ns:AddressLine1>
<ns:AddressLine2>?</ns:AddressLine2>
<ns:City>?</ns:City>
<ns:Country>?</ns:Country>
<ns:Email>?</ns:Email>
<ns:FirstName>?</ns:FirstName>
<ns:HomePhone>?</ns:HomePhone>
<ns:ID>?</ns:ID>
<ns:LastName>?</ns:LastName>
<ns:MobilePhone>?</ns:MobilePhone>
<ns:State>?</ns:State>
<ns:Suburb>?</ns:Suburb>
</Data>
**</AddCustomer>**
To
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:biz="http://BizTalk.Test001.Customer">
<soapenv:Header/>
<soapenv:Body>
<biz:Data>
<ID>?</ID>
<FirstName>?</FirstName>
<LastName>?</LastName>
<MobilePhone>?</MobilePhone>
<HomePhone>?</HomePhone>
<Email>?</Email>
<AddressLine1>?</AddressLine1>
<AddressLine2>?</AddressLine2>
<Suburb>?</Suburb>
<City>?</City>
<State>?</State>
<Country>?</Country>
</biz:Data>
Basically i want to get rid of "AddCustomer" from the request. Can we do something using ServiceContract or OperationContract.
You have to implement IClientMessageInspector interface.So before sending a request or after receving the response you need to implement your own C# methods to override your request /response SOAP messages like,
public class ClientMessageInspector : IClientMessageInspector
{
public string LastRequestXml { get; private set; }
public string LastResponseXml { get; private set; }
public void AfterReceiveReply(ref Message reply, object correlationState)
{
LastResponseXml = reply.ToString();
}
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
LastRequestXml = request.ToString();
var xmlPayload = ChangeMessage();
var ms = new MemoryStream();
var writer = new StreamWriter(ms);
writer.Write(xmlPayload);
writer.Flush();
ms.Position = 0;
var reader = XmlReader.Create(ms);
request = Message.CreateMessage(reader, int.MaxValue, request.Version);
var payload = request.ToString();
return request;
}
/// Manipulate the SOAP message
private string ChangeMessage()
{
//manipulation code here to remove AddCustomer.
}
}
For detail implementation check out this blog.
https://cmatskas.com/changing-soap-message-data-and-namespaces-with-c/
Related
I'm writing a soap web service in asp.net core using soapcore to replace an existing web service. the caller's request xml cannot change because we intend to minimize change on that side.
the current request xml looks like
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://vendornamespace.com/"
xmlns:idat="http://schemas.datacontract.org/2004/07/iDataContract">
<soapenv:Header/>
<soapenv:Body>
<ser:ActionRequest>
<ser:composite>
<idat:param1>H04</idat:param1>
<idat:param2>100</idat:param2>
</ser:composite>
</ser:PlaceOrder>
</soapenv:Body>
</soapenv:Envelope>
my web service Interface looks like
public interface INewWebsServices
{
[OperationContract(Action = "ActionRequest")]
Task<WebSvcResponseClass> ActionRequest([MessageParameter(Name = "composite")] WebServiceReqactionMethod_A);
}
and my request class looks like
[DataContract (Namespace = "http://schemas.datacontract.org/2004/07/iDataContract", Name ="idat")]
[MessageContract()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.datacontract.org/2004/07/iDataContract")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.datacontract.org/2004/07/iDataContract", IsNullable = false)]
public class WebServiceReq
{
[MessageBodyMember]
public string param1 { get; set; }
[MessageBodyMember]
public string param2 { get; set; }
}
this generates a web service request :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://vendornamespace.com/">
<soapenv:Header/>
<soapenv:Body>
<ser:PlaceOrder>
<ser:composite>
<ser:param1>?</ser:param1>
<ser:param2>?</ser:param2>
</ser:composite>
</ser:PlaceOrder>
</soapenv:Body>
</soapenv:Envelope>
Clearly the idat namespace is missing in the header and is not used in the web request parameters.
How do I modify my WebServiceReq class to include the missing namespace so that SoapCore can de serialize the incoming request properly.
You can try to define WebSvcResponseClass in a namespace iDataContract, also decorate the properties with [DataMember] and not [MessageBodyMember].
Also, you may define your service interface as
public interface INewWebsServices
{
[System.ServiceModel.OperationContractAttribute(Action = "http://domain/namespace", ]
Task<iDataContract.WebSvcResponseClass> WebServiceReqactionMethod_A(iDataContract.WebserviceRequestclass composite);
}
using the namespace generates idat as prefix in the web service request if you try it using soapUI.
I am consuming a SOAP service, and successfully receiving a response.
Unfortunately, WCF isn't deserialising the members of the response POCO.
Using Diagnostics Tracing, I get the following error occurring for each member in the Response:
Description An unrecognised element was encountered in the XML during deserialisation which was ignored.
Element :partnerId
Please can you see where I am going wrong?
Here's the code:
Service Contract
[ServiceContract(Namespace = "https://foo.com/services/mediationV1/", ConfigurationName = "IPlatformPortType")]
public interface IPlatformPortType
{
[OperationContract(Action = "https://foo.com/services/foo_bar_HeartBeatV1/", ReplyAction = "*")]
HeartBeatResponse SendHeartBeat(HeartbeatRequest request);
}
POCO Request
[MessageContract(IsWrapped = true,
WrapperName = "foo_bar_HeartBeatRequest",
WrapperNamespace = "https://foo.com/schemas/fooV1/")]
public class HeartbeatRequest
{
[MessageBodyMember(Namespace = "https://foo.com/schemas/fooV1/", Order = 0)]
public string transactionId { get; set; }
[MessageBodyMember(Namespace = "https://foo.com/schemas/fooV1/", Order = 2)]
public string partnerId { get; set; }
}
SOAP Request
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">https://foo.com/services/foo_bar_HeartBeatV1/</a:Action>
<a:MessageID>urn:uuid:73efa49e-f2b3-4f5d-b750-6784b1becfbc</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
</s:Header>
<s:Body>
<foo_bar_HeartBeatRequest xmlns="https://foo.com/schemas/fooV1/">
<transactionId>f642df34-d328-4d76-9a9f-3cf64bdef71d</transactionId>
<partnerId>bob</partnerId>
</foo_bar_HeartBeatRequest>
</s:Body>
</s:Envelope>
POCO Response
[MessageContract(IsWrapped = true,
WrapperName = "foo_bar_HeartBeatResponse",
WrapperNamespace = "https://foo.com/schemas/fooV1/")]
public class HeartBeatResponse
{
[MessageBodyMember(Namespace = "https://foo.com/schemas/fooV1/", Order = 2)]
public string transactionId { get; set; }
[MessageBodyMember(Namespace = "https://foo.com/schemas/fooV1/", Order = 3)]
public int requestStatus { get; set; }
}
SOAP Response
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:MessageID>urn:uuid:b66497fd-4e95-458c-8772-c8bfc0decb51</wsa:MessageID>
<wsa:Action>urn:mediateResponse</wsa:Action>
<wsa:RelatesTo>urn:uuid:73efa49e-f2b3-4f5d-b750-6784b1becfbc</wsa:RelatesTo>
</soapenv:Header>
<soapenv:Body>
<ns:foo_bar_HeartBeatResponse xmlns:ns="https://foo.com/schemas/fooV1/">
<transactionId>f642df34-d328-4d76-9a9f-3cf64bdef71d</transactionId>
<requestStatus>1</requestStatus>
</ns:foo_bar_HeartBeatResponse>
</soapenv:Body>
</soapenv:Envelope>
Update
As per #TomRedfern's comment, I tried swapping out all the MessageContract attributes with DataContract.
Unfortunately this takes away my ability to notify the seriliaser that the POCO is a wrapper, causing the SOAP to look like the following:
<s:Body>
<SendHeartBeat xmlns="https://foo.com/services/fooV1/">
<request xmlns:b="https://foo.com/schemas/fooV1/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<b:transactionId>7085587a-4275-49ad-9aa9-5b413afcbe62</b:transactionId>
<b:partnerId>bob</b:partnerId>
</request>
</SendHeartBeat>
</s:Body>
Notice the name of the node is now the name of the Operation (SendHeartBeat), and it's child node is the parameter name of that operation.
I do not have control over the WSDL, so this will not work.
After a day's worth of trial and error attempts, I've finally got the deserialisation working correctly.
For each of the POCO's members I did the following:
add an XmlElementAttribute.
add the Form parameter to XmlElementAttribute, with value of XmlSchemaForm.Unqualified.
remove the Namespace parameter from the MessageBodyMemberAttribute and add to the XmlElementAttribute.
removed any existing parameters in MessageBodyMemberAttribute.
Here is the correct response POCO:
[MessageContract(IsWrapped = true,
WrapperName = "foo_bar_HeartBeatResponse",
WrapperNamespace = "https://foo.com/schemas/fooV1/")]
public class HeartBeatResponse
{
[MessageBodyMember]
[XmlElement(Form = XmlSchemaForm.Unqualified, Namespace = "https://foo.com/schemas/fooV1/")]
public string transactionId { get; set; }
[MessageBodyMember]
[XmlElement(Form = XmlSchemaForm.Unqualified, Namespace = "https://foo.com/schemas/fooV1/")]
public int requestStatus { get; set; }
}
I'm currently testing out writing a RESTful API with ASP.NET Web API. I'm using RestSharp on a client to simulate different calls.
I want to submit an application ID query string, and the body should be a collection of type "Log". Every time, the application ID get's posted by the body received by the server is always NULL.
Code on the server:
public class LogsController : ApiController
{
public HttpStatusCode Post(Guid ID, [FromBody] List<Log> logs)
{
if (logs != null)
return HttpStatusCode.OK;
else
return HttpStatusCode.PreconditionFailed;
}
}
public class Log
{
public Guid ErrorId { get; set; }
}
Code on the client:
static void Main(string[] args)
{
var client = new RestClient("http://localhost:36146/api");
var json = JsonConvert.SerializeObject(new List<Log>()
{
new Log { ErrorId = Guid.NewGuid()}
});
var request = new RestRequest("Logs", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddParameter("ID", Guid.NewGuid(), ParameterType.QueryString);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/json; charset=utf-8");
request.AddBody(json);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Console.Read();
}
public class Log
{
public Guid ErrorId { get; set; }
}
I thought I got this working, however no matter what I do now the "logs" parameter on the server is always NULL.
I think I've found the issue.
RestSharp implicitly uses the JsonSerializer when populating the body of the request. As I was also called the Serializer I think it caused issues with the formatting.
I've removed that call to the serializer and now I'm receiving a 200 back from the server.
Happy days.
I wrote a simple test client a while back to test out my XML payload, but my service keeps giving me back 400 Bad Requests. The following is my DataContract, followed by an example payload:
[DataContract(Name = "TMGTLabel", Namespace = "http://Novartis.TMGT.TMGTLabelPrint")]
public class TMGTLabel
{
[DataMember(Name = "ProposedfileName")]
public string proposedfilename;
[DataMember(Name = "LabelDataGroup")]
public LabelData ldatagrp;
}
[DataContract(Namespace = "http://Novartis.TMGT.TMGTLabelPrint")]
public class LabelData
{
[DataMember(Name = "ldata")]
public string[] ldata;
}
<TMGTLabel>
<ProposedfileName>TestLabelFileName</ProposedfileName>
<LabelDataGroup>
<LabelData><ldata>sample 1</ldata></LabelData>
<LabelData><ldata>sample 2</ldata></LabelData>
<LabelData><ldata>sample 3</ldata></LabelData>
<LabelData><ldata>sample 4</ldata></LabelData>
<LabelData><ldata>sample 5</ldata></LabelData>
<LabelData><ldata>sample 6</ldata></LabelData>
<LabelData><ldata>sample 7</ldata></LabelData>
</LabelDataGroup>
</TMGTLabel>
Is there anything wrong with my payload according to how I have my DataContract designed?
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> { }