SoapFormatter - how to include namespaces? - c#

I have a blocker which I cannot solve. Issue is in auto-generated SOAP request built by SoapFormatter class. I'm trying to communicate with WCF service and pass some data. I've implemented class which I am trying to serialize to soap request.
[Serializable]
public class MySoapClass: ISerializable
{
public string Username{ get; set; }
public string Password{ get; set; }
public int Data3 { get; set; }
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.FullTypeName = "ThisIsNameIWantInSoap";
info.AddValue("Username", Username);
info.AddValue("Password", Password);
info.AddValue("Data3", Data3);
}
}
I'm using MemoryStream and MySoapClass object in SoapFormatter. I am getting soap string this way Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Position)
Generated soap string does not work, request is delivered, but I am getting "authentication error", just like WCF service could not extract any data from request.
This is auto-generated soap string:
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:ThisIsNameIWantInSoap id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/assem/http://tempuri.org/">
<Username id="ref-1">username</Username>
<Password id="ref-2">password</Password>
<Data3>10</Data3>
</a1:ThisIsNameIWantInSoap>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
After copying soap string to SoapUI and adding namespace tag to every parameter, everything works fine. I am getting proper response from WCF service.
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:ThisIsNameIWantInSoap id="ref-1" xmlns:a1="http://tempuri.org/">
<a1:Username id="ref-1">username</a1:Username>
<a1:Password id="ref-2">password</a1:Password>
<a1:Data3>10</a1:Data3>
</a1:ThisIsNameIWantInSoap>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
My questions are:
-How to auto-generate soap string which includes "a1:" namespace tag in every parameter?
-(Answered) How to change "a1:" namespace to"somethingElse:"?

Related

Change XML Tag Prefix SOAP

I am trying to create a SOAP message with the prefix. however, I am having trouble setting the namespace correctly. I have been trying for days and tried many suggestions I found online, but none seem to work. I am hoping some of you can help me.
What I'm getting is
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Transaction xmlns="http://tempuri.org/">
<bankingTransaction>
<operation parameterOrder="string">
<fault />
<fault />
</operation>
<transactionDate>dateTime</transactionDate>
<amount>int</amount>
</bankingTransaction>
</Transaction>
</soap:Body>
</soap:Envelope>
& what I actually need is
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<res:Transaction xmlns="res:http://tempuri.org/">
<res:bankingTransaction>
<res:operation parameterOrder="string">
<res:fault />
<res:fault />
</res:operation>
<res:transactionDate>dateTime</res:transactionDate>
<res:amount>int</res:amount>
</res:bankingTransaction>
</res:Transaction>
</soap:Body>
</soap:Envelope>
& My MassageContact is
[MessageContract]
public class BankingTransaction
{
[MessageHeader] public Operation operation;
[MessageHeader] public DateTime transactionDate;
[MessageBodyMember] private unit sourceAccount;
[MessageBodyMember] public int amount;
}
Please Help me to add prefix with my XML Elements.
Thanks
You probably need to do something like this:
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://tempuri.org/")]
[MessageContract]
public class BankingTransaction
{
[MessageHeader] public Operation operation;
[MessageHeader] public DateTime transactionDate;
[MessageBodyMember] private unit sourceAccount;
[MessageBodyMember] public int amount;
}
I am not sure how you are serializing your objects, but something like this will add the prefix:
XmlSerializerNamespaces x = new XmlSerializerNamespaces();
x.Add("res", "http://tempuri.org/");
add the XmlSerializerNamespaces to you serialization process maybe? It's hard to say without seeing what else you are doing. All your contracts/classes in that namespace probably need this attribute: [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://tempuri.org/")]
We could create a MessageFormatter to customize the message format, you could refer to the following official tutorial.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/extending/custom-message-formatters
Here is an example about how to do this.
https://stackoverflow.com/questions/31595770/c-sharp-wcf-global-namespaces-royal-mail/31597758#31597758
http://vanacosmin.ro/Articles/Read/WCFEnvelopeNamespacePrefix

WCF Soap response is wrapping properties

This WCF response seems to be wrapping the property WhoAmIResult more than once.
Response:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<WhoAmIResponse xmlns="http://tempuri.org/">
<WhoAmIResult xmlns:a="http://schemas.datacontract.org/2004/07/Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:WhoAmIResult>your name here</a:WhoAmIResult>
</WhoAmIResult>
</WhoAmIResponse>
</s:Body>
</s:Envelope>
Expectation:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<WhoAmIResponse xmlns="http://tempuri.org/">
<a:WhoAmIResult>your name here</a:WhoAmIResult>
</WhoAmIResponse>
</s:Body>
</s:Envelope>
Here's the code behind:
[ServiceContract]
public interface IService
{
[OperationContract]
WhoAmIResponse WhoAmI(string s);
}
[DataContract]
public class WhoAmIResponse
{
[DataMember]
public string WhoAmIResult { get; set; }
}
public class Service : IService
{
public WhoAmIResponse WhoAmI(string s)
{
return new WhoAmIResponse
{
WhoAmIResult = s
};
}
}
I just can't figure out what I need to do to get to this response, without exceeding wrappers. I must not have WhoAmIResult twice in the answer.
You can try setting setting Namespace to empty
[DataContract(Namespace = "")]
public class WhoAmIResponse
{
[DataMember]
public string WhoAmIResult { get; set; }
}
When I took out the namespace as #STORM told me to, I've got this response:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<WhoAmIResponse xmlns="http://tempuri.org/">
<WhoAmIResult xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<WhoAmIResult xmlns="">your name result</WhoAmIResult>
</WhoAmIResult>
</WhoAmIResponse>
</s:Body>
</s:Envelope>
The namespace wasn't there but there was still the wrapper.
After some experiments I noticed that WhoAmIResult could be the name of a string response not a object. So I removed WhoAmIResponse object and put a simple string response. The code ended up like this:
public class MyService : IMyService
{
public string WhoAmI(string s)
{
return s;
}
}
And now I get the expected response:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<WhoAmIResponse xmlns="http://tempuri.org/">
<WhoAmIResult>your name result</WhoAmIResult>
</WhoAmIResponse>
</s:Body>
</s:Envelope>
The problem was that I didn't need to declare another object to give the answer. There seems to be a convention for the response XML like [MethodName]Response and [MethodName]Result.
Thanks #STORM, I'm ashamed.

SOAP Web service custom soap xml response

This is my XML response soap (WS in c# ".asmx") :
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<TestrequestResponse xmlns="http://tempuri.org/">
<TestrequestResult>
<name>filename.xml</name>
<code>30</code>
</TestrequestResult>
</TestrequestResponse>
</soap:Body>
</soap:Envelope>
I want to have a response like this :
<?xml version="1.0" encoding="utf-8"?>
<Response result=”KO”>
<name>filename.xml</name>
<code>30</code>
</Response>
how can I do this?
Thank you very much for your help.
edit :
[WebMethod]
public Response Testrequest()
{
var r = new Response();
r.name = "30";
r.code = "0";
return r;
}
object response :
public class Response
{
public string name { get; set; }
public string code { get; set; }
}
You may do SOAP message formatting ( https://msdn.microsoft.com/en-us/library/dkwy2d72%28v=vs.100%29.aspx ), but creating a simple web page returning the desired format instead of calling a SOAP service is easier.

NServiceBus generates XML message without simple type namespaces

I'm currently upgrading from NServiceBus 4.7.5 to NServiceBus 5.2.4, and when using the XmlSerializer, I noticed that it generates an invalid XML message when I try to serialize a message -
the namespaces for the simple types are no longer declared, but they're still used in the document.
For example, if I try to serialize a message that exposes a SerializedPair property that is defined as follows:
public class SerializedPair
{
public string Key { get; set; }
public object Value { get; set; }
}
in 4.7.5 it would be serialized as:
<?xml version="1.0"?>
<Messages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://tempuri.net/xxx.Crm.ServiceBusMessages"
xmlns:guid="Guid" xmlns:int32="Int32"
xmlns:string="String"
xmlns:datetime="DateTime"
xmlns:boolean="Boolean"
xmlns:decimal="Decimal">
<UpdateContact>
<SerializedPair>
<Key>AddressId</Key>
<guid:Value>ebdeeb33-baa7-4100-b1aa-eb4d6816fd3d</guid:Value>
</SerializedPair>
....
In 5.2.4, it gets serialized as:
<?xml version="1.0" ?>
<UpdateContact xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://tempuri.net/xxx.Crm.ServiceBusMessages">
<SerializedPair>
<Key>AddressId</Key>
<guid:Value>70a22cd7-64fd-4d6d-ab13-2ad7800addc7</guid:Value>
</SerializedPair>
....
Note that the "Messages" element is no longer present, and the "xmlns:guid" namespace declaration is also missing, which makes this invalid XML.
How do I get NServiceBus to generate valid xml again?
For completeness, my BusConfiguration looks like:
BusConfiguration busConfiguration = new BusConfiguration();
busConfiguration.UseSerialization<XmlSerializer>();
busConfiguration.UseTransport<MsmqTransport>();
ISendOnlyBus bus = Bus.CreateSendOnly(busConfiguration);

Manipulating WCF header details

I am building a client to some STS service and for more than one day now I am trying to add a Header to a WCF message. In my call to RequestSecurityToken I have to include a UsernameToken.
I'm not sure how to accomplish that. For the moment I defined an endpoint behavior and a message inspector (took me long enough to discover those...). In the BeforeSendRequest() of the latter I create an object of the custom class 'Security' which derives from MessageHeader. Security includes an instance of UsernameToken.
public class MessageInspector : IClientMessageInspector {
public object BeforeSendRequest(ref Message request, IClientChannel channel) {
Security uns = new Security();
uns.UsernameToken = new UsernameToken();
// ...
var Header = new MessageHeader<Security>(uns);
var untyped = Header.GetUntypedHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
request.Headers.Add(untyped);
return null;
}
}
public class Security : MessageHeader {
public UsernameToken UsernameToken = new UsernameToken();
public override string Name {
get { return "Security"; }
}
public override string Namespace {
get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; }
}
}
public class UsernameToken {
public String Username = "";
public Password Password = new Password();
}
This is what is being serialised
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">urn:RequestSecurityToken</Action>
<Security xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<UsernameToken xmlns="http://schemas.datacontract.org/2004/07/Tarifrechner.Kfz">
<Password>
<Type>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText</Type>
<password>******</password>
</Password>
<Username>******</Username>
</UsernameToken>
</Security>
</s:Header>
<s:Body />
</s:Envelope>
Especially the namespace of UsernameToken seems to be wrong. I know it comes from the data contract serialization but i need a different namespace.
This is what I would like the serialised data to look like
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="...">
<soap:Header>
<Security xmlns:q1="http://www.bipro.net/namespace" xsi:type="q1:UserNameSecurity"
xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<UsernameToken>
<Username>******</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">******</Password>
</UsernameToken>
</Security>
<wsa:Action>urn:RequestSecurityToken</wsa:Action>
<wsse:Security>
<wsu:Timestamp wsu:Id="Timestamp-b9dd599d-5901-451d-8321-6a309091f273">
<wsu:Created>2012-03-11T16:02:56Z</wsu:Created>
<wsu:Expires>2012-03-11T16:07:56Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body>
<RequestSecurityToken xmlns="http://schemas.xmlsoap.org/ws/2005/02/trust">
<TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</TokenType>
<RequestType>
http://schemas.xmlsoap.org/ws/2005/02/trust/Issue
</RequestType>
</RequestSecurityToken>
</soap:Body>
</soap:Envelope>
Is my approach about right? And how can I manipulate things like the namespace of a header detail or whether data is being serialised as attribute or element?
Update
As Ladislav already noted, I don't have to implement classes like UsernameToken myself. I did that only because my knowledge of WCF is so limited.
By now, I discovered, that WS2007HttpBinding, configured to use SecurityMode.TransportWithMessageCredential and with EstablishSecurityContext set to false produces almost the XML I am looking for. How should I have known that?
But there's one problem left: My request has an empty body element, where the request I want to produce, features a RequestSecurityToken element inside the body element. Does anyone know, how I can achieve that?
Using EstablishSecurityContext = true helps but at the same time changes my Soap-Action from the desired "urn:RequestSecurityToken" into the non-working "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/SCT".
I appreciate any answer!
Thanks a lot!
Björn
One alternative way is to define a MessageContract type for your request, which allows you to define what shows up in the header and body of the SOAP message and adjust the namespace used. For example, consider the following service definition:
[ServiceContract]
public interface IMyService
{
[OperationContract]
MyResponse DoSomething(MyRequest request);
}
public class MyService : IMyService
{
public MyResponse DoSomething(MyRequest request)
{
return new MyResponse()
{
Details = "Service did something awesome.",
Timestamp = DateTime.Now
};
}
}
[MessageContract(IsWrapped = true, WrapperNamespace = "http://myservice/messages/")]
public class MyRequest
{
[MessageHeader(Namespace = "http://myservice/security")]
public string TokenThingy
{
get;
set;
}
}
[MessageContract(IsWrapped = true, WrapperNamespace = "http://myservice/messages")]
public class MyResponse
{
[MessageBodyMember]
public string Details
{
get;
set;
}
[MessageBodyMember]
public DateTime Timestamp
{
get;
set;
}
}
Sending a request produces the following SOAP:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IMyService/DoSomething</Action>
<h:TokenThingy xmlns:h="http://myservice/security">fda</h:TokenThingy>
</s:Header>
<s:Body>
<MyRequest xmlns="http://myservice/messages/" />
</s:Body>
</s:Envelope>
And a response from the service looks like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body>
<MyResponse xmlns="http://myservice/messages">
<Details xmlns="http://tempuri.org/">Service did something awesome.</Details>
<Timestamp xmlns="http://tempuri.org/">2012-05-04T17:04:36.5980424-04:00</Timestamp>
</MyResponse>
</s:Body>
</s:Envelope>
There are a few different ways to add headers to a message depending on how you need to control the header content and where you need to insert the header.
In your application code you can create an OperationContextScope around the request in order to change some of the request properties. Inside an OperationContextScope, you have a valid instance of OperationContext.Current, which allows manipulation of the message headers through the OutgoingMessageHeaders collection. Use of this method deeply bakes control of the headers into the application code. You would have to be responsible for copying the appropriate code wherever it was needed.
These two links (from someone on the WCF team) talk about this in greater detail with a ton of code samples:

Categories