Configure WCF service to accept unqualified parameter - wcf

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>

Related

How are SOAP web service namespaces customized

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.

svcutil get wsdl and xsd from dll

Launching in Command Prompt
svcutil "C:\Users\...\Documents\Visual Studio 2015\Projects\WcfServiceLibrary1\WcfServiceLibrary1\bin\Debug\WcfServiceLibrary1.dll"
Output:
Microsoft (R) Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 4.6.1055.0]
Copyright (c) Microsoft Corporation. All rights reserved.
Generating metadata files...
C:\Windows\System32\tempuri.org.wsdl
C:\Windows\System32\tempuri.org.xsd
C:\Windows\System32\schemas.microsoft.com.2003.10.Serialization.xsd
C:\Windows\System32\WcfServiceLibrary1.xsd
But it seems that there are no any signs of files in C:\Windows\System32\ where svcitul put them?
Interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WcfServiceLibrary1
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
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.
// You can add XSD files into the project. After building the project, you can directly use the data types defined there, with the namespace "WcfServiceLibrary1.ContractType".
[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; }
}
}
}
Implmentation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WcfServiceLibrary1
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
}
For svcutil command in C:\Windows\System32, the files are created in C:\Windows\SysWOW64.
you could also use the /directory to assign the output folder.
here is official document.
https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-3.5/aa347733(v=vs.90)

WCF Contract Generation

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/

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

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