How are SOAP web service namespaces customized - asp.net-core

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.

Related

WCF - MessageContract members (MessageBodyMember) ignored when deserialising

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; }
}

Namespace issue with wcf Restful service

This is my first post.
I am building a restful wcf service using post to accept an XML message (truncated from the real one). I am having trouble getting WCF to parse the XML due to the way the message is using namespaces. I cannot change the format of the XML message.
I have tried various combinations of namespace attributes on the Service and datacontract but either get a parsing error or segments that are missing or NULL.
If I was able to change the message I can get it to work by either removing the namespace or by applying the namespace prefix to all the fields. Unfortunately, it is not possible to get the vendor to change the format of the message being sent.
Is there a way to get this to work with the message being sent.
Sample Request
<m:MYMESSAGE xmlns:m="my.report">
<MESSAGEHEADER>
<MESSAGETYPE>GoodReport</MESSAGETYPE>
<MESSAGEDATE>20160203134445</MESSAGEDATE>
<MESSAGEACTION>UPDATE</MESSAGEACTION>
</MESSAGEHEADER>
<PATIENT>
<LASTNAME>Last</LASTNAME>
<FIRSTNAME>First</FIRSTNAME>
<MIDDLENAME>Middlename</MIDDLENAME>
</PATIENT>
</m:MYMESSAGE>
Sample Incorrect Response
<MYMESSAGE xmlns="my.report" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<MESSAGEHEADER i:nil="true"/>
<PATIENT i:nil="true"/>
</MYMESSAGE>
WCF Code
[ServiceContract]
public interface IPDF
{
[OperationContract ]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Xml
)]
MYMESSAGE GetPdf(MYMESSAGE mymessage);
}
[DataContract(Name = "MYMESSAGE", Namespace = "my.report")]
public class MYMESSAGE
{
[DataMember (Name ="MESSAGEHEADER",Order=0) ]
public _MESSAGEHEADER MESSAGEHEADER { get; set; }
[DataMember(Name = "PATIENT", Order = 1)]
public _PATIENT PATIENT { get; set; }
}
[DataContract(Namespace = "my.report")]
public class _MESSAGEHEADER
{
[DataMember(Name = "MESSAGETYPE", Order = 0)]
public string MESSAGETYPE { get; set; }
[DataMember(Name = "MESSAGEDATE", Order = 1)]
public string MESSAGEDATE { get; set; }
}
You can use Message Contracts to create the shape of the message you need. For eaxample:
[MessageContract]
public class BankingTransaction
{
[MessageHeader] public Operation operation;
[MessageHeader(Namespace="http://schemas.contoso.com/auditing/2005")] public bool IsAudited;
[MessageBodyMember(Name="transactionData")] public BankingTransactionData theData;
})
WCF uses Message based on SOAP but WCF internals can hide this by converting inbound messages to SOAP and outbound messages to what ever transport protocol you are using.
You can ultimately create your own message formatter.
"Message formatters are the component which do the translation between CLR operations and the WCF Message object – their role is to convert all the operation parameters and return values (possibly via serialization) into a Message on output, and deconstruct the message into parameter and return values on input."

How to return a specific SOAP response from a WCF service?

I have a WCF method which currently returns a string like so:
string CreateDesign(string UID, string TemplateName)
The retruned string is created by this method:
string RequestProcessed(string status, string UID)
{
XDocument doc = new XDocument(
new XElement("RequestProcessed",
new XElement("Status", status),
new XElement("UID", UID)));
return doc.ToString();
}
The response that I got from using SOAPUI is like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<CreateDesignResponse xmlns="http://tempuri.org/">
<CreateDesignResult><![CDATA[<RequestProcessed>
<Status>OK</Status>
<UID>FolderName</UID>
</RequestProcessed>]]></CreateStandardResult>
</CreateStandardResponse>
</s:Body>
</s:Envelope>
The client is expecting it like this:
<soap:Body>
<m:RequestProcessed xmlns:m=" ">
<m:Status></m:Status>
<m:UID></m:UID>
</m: RequestProcessed >
</soap:Body>
What should I do to make the WCF service return the response like that?
I am struggling with this for quite sometime now, appreciate any help.
Regards.
To have full controll over your SOAP message body you should use MessageContract attribute for both your input and output parameters. Here is a good article on the web to start with.
Using Message Contracts
Hope it helps!
EDITED:
Operation contract
[ServiceContract]
public interface ITestService
{
[OperationContract]
RequestProcessed TestMethod(RequestInput tt);
}
RequestProcessed
[MessageContract]
public class RequestProcessed
{
[MessageBodyMember]
public string Status { get; set; }
[MessageBodyMember]
public Guid UID { get; set; }
}
SOAP message body
<s:Body>
<RequestProcessed xmlns="http://tempuri.org/">
<Status>OK</Status>
<UID>ffd338ed-bca3-46c1-9ee3-3c92ba3b3acc</UID>
</RequestProcessed>
</s:Body>

WCF with ws-address: creating bare parameters

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; }
}

Configure WCF service to accept unqualified parameter

My WCF service does not recognize request parameter values that are sent in unqualified form and substitutes default values instead.
For example, this request will yield a result of "You entered: 21".
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sam="http://www.example.org/SampleService/">
<soapenv:Header/>
<soapenv:Body>
<sam:GetData>
<sam:value>21</sam:value>
</sam:GetData>
</soapenv:Body>
</soapenv:Envelope>
But the response to this request is "You entered: 0".
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sam="http://www.example.org/SampleService/">
<soapenv:Header/>
<soapenv:Body>
<sam:GetData>
<value>21</value>
</sam:GetData>
</soapenv:Body>
</soapenv:Envelope>
How can I modify my service so that both types of requests will use the value I send in?
IService1.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WcfServiceLibrary2
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config.
[ServiceContract(Namespace = "http://www.example.org/SampleService/", Name = "sampleservice")]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
}
// Use a data contract as illustrated in the sample below to add composite types to service operations
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}
Service1.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WcfServiceLibrary2
{
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
}
Those two requests are not equivalent XML and don't conform to the service WSDL. The "unqualified" request will parse the value element into the default XML namespace which will depend on the overall XML for that request. WCF doesn't understand value as being part of the soap envelop XML and can't match it to any DataContract class. You can try making the default XML namespace be your service XML namespace as shown below and see if the service will then process it correctly:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns="http://www.example.org/SampleService/">
<soapenv:Header/>
<soapenv:Body>
<GetData>
<value>21</value>
</GetData>
</soapenv:Body>
</soapenv:Envelope>