How to view SOAP message to check the IsWrapped in MessageContract attribute, as the example below:
[MessageContract]
public class HelloGreetingMessage
{
private string localGreeting;
[MessageBodyMember(
Name = "Salutations",
Namespace = "http://www.examples.com"
)]
public string Greeting
{
get { return localGreeting; }
set { localGreeting = value; }
}
}
How to view this:
/*
The following is the request message, edited for clarity.
<s:Envelope>
<s:Header>
<!-- Note: Some header content has been removed for clarity.
<a:Action>http://GreetingMessage/Action</a:Action>
<a:To s:mustUnderstand="1"></a:To>
</s:Header>
<s:Body u:Id="_0" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<HelloGreetingMessage xmlns="Microsoft.WCF.Documentation">
<Salutations xmlns="http://www.examples.com">Hello.</Salutations>
</HelloGreetingMessage>
</s:Body>
</s:Envelope>
*/
You can use tools like Fiddler or FireShark to inspect your request and response. You can also debug Https traffic.
Related
I have a WCF client calling a Java SOAP service (with TLS and MA incidentally).
The SOAP action is coming out as:
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
But I am trying to set it to
<a:Action s:mustUnderstand="1">urn:example:services:201005:SendMessage</a:Action>
I thought I could do this using the following OperationContract attribute...
[ServiceContract(Namespace = "urn:example:ns:201005", ConfigurationName = "IExampleService")]
public interface IExampleService : IDisposable
{
[OperationContract(Action = "urn:example:services:201005:SendMessage")]
[FaultContract(typeof(ExampleErrorInfo), Action = "urn:example:services:201005:SendMessage", Name = "ExampleErrorInfo")]
[XmlSerializerFormat(SupportFaults = true)]
ExampleResponse SendMessage(ExampleRequest request);
}
Why is my SOAP action still wrong?
This seems to be a WS-Trust handshake message, not your actual message. See http://schemas.xmlsoap.org/ws/2005/02/trust/tls/WSTrustForTLS.pdf , section 1.8.
I am attempting to hit a WSE secured web service using WCF. The SOAP header I need to generate to do this should contain a username, password, nonce, and created date... Here is a an example of a soap UI header that I use to hit the same service...
<soap:Header>
<wsse:Security soap:mustUnderstand="true" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-2" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>----------</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">----------</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">Hozef94FFwOhuiF5QixaMQ==</wsse:Nonce>
<wsu:Created>2012-08-21T13:26:03.642Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
Now I found a tutorial that was pretty usefull. usefull tutorial
I have successfully implemented it... But I am now sticking the SOAP body into the soap header and there isn't a nonce generated.
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo9VZylDHg5JMgjsNnWLhATkAAAAA+YtOxHdh0Uqd4a64raX/nIzYz20mPHlBv4Wk5S8d5PsACQAA</VsDebuggerCausalityData>
<wsse:Security s:mustUnderstand="0" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<UsernameToken xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<Username>------------</Username>
<Password>************</Password>
</UsernameToken>
</wsse:Security>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetOrganizations xmlns="http://------------------------/businessobjects/messaging/">
<personId xmlns="">0</personId>
<typeId xmlns="">
<int>1</int>
<int>2</int>
<int>3</int>
<int>4</int>
</typeId>
</GetOrganizations>
</s:Body>
</s:Header>
</s:Envelope>
Don't know why it is doing this. I followed the tutorial to the letter. My Repository getting everything...
using (DistListServiceReference.DistributionListClient dlc = new DistListServiceReference.DistributionListClient())
{
try
{
PasswordDigestBehavior behavior = new PasswordDigestBehavior("********", "********");
dlc.Endpoint.Behaviors.Add(behavior);
GetDistributionLists gdl = new GetDistributionLists();
gdl.PersonID = 0;
GetDistributionListsResponse gdlr = new GetDistributionListsResponse();
gdlr = dlc.GetDistributionLists(gdl);
return gdlr;
}
catch (Exception e)
{
dlc.Abort();
return null;
}
}
My PasswordDigentInspector
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
// Use the WSE 3.0 security token class
UsernameToken token = new UsernameToken(this.Username, this.Password, PasswordOption.SendPlainText);
WseHeader header = new WseHeader(this.Username, this.Password);
// Serialize the token to XML
XmlElement securityToken = token.GetXml(new XmlDocument());
MessageHeader securityHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken", securityToken, false);
request.Headers.Add(header);
// complete
return Convert.DBNull;
}
How I apply client behavior
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new PasswordDigestMessageInspector(this.Username, this.Password));
}
Everything is pretty much there. I am not seing where the body is being injected into the header. Any body have any ideas?
UPDATE: Debugging
I am looking at the the actual header that I inject into the soap message and this is what I see...
{<wsse:Security s:mustUnderstand="0" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<UsernameToken xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<Username>**********</Username>
<Password>************</Password>
</UsernameToken>} System.ServiceModel.Channels.MessageHeaderInfo {TestDistListApplication.Repository.WseHeader}
Just looking at that, there is no nonce, I could build it programmatically, but I am not sure if that is a good idea. Especially since there is a nonce already and a create date in the securityToken... Not sure why they don't appear in the header though...
<wsse:Username xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">MedTrak_Dev</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">uncJUN132012</wsse:Password>
<wsse:Nonce xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">mvy9nUfF+rnT3oTasDBqxg==</wsse:Nonce>
<wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2012-08-28T13:30:42Z</wsu:Created>
I have the debugger stopped and I am watching both those variables. What might cause the header not to have those things and the the WSEHeader to have them?
Looks like I will have to debugg that.
Without running your code myself it is a little tricky to understand why my example doesn't work in your scenario. However, in a attempt to debug this I would try the following:
Stick a breakpoint on this line:
XmlElement securityToken = token.GetXml(new XmlDocument());
And after stepping through this line, see what the securityToken XML looks like. It is this XML which is being injected into the security header. It isn't any more complicated than that. You coudl create this XML manually if you want. I used the WSE 3.0 classes because I didn't want to hand code the XML myself.
I also notice that your security token definition looks slightly different to mine. My example was:
MessageHeader securityHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", securityToken, false);
Yours:
MessageHeader securityHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken", securityToken, false);
The namespaces are different. Any reason for this?
If the breakpoint added from the step above isn't hit then it would suggest that the behavior isn't actually being applied. In which case, you'll probably need to double check your configs to make sure that the behavior is correctly applied on the client. Alternatively (as my example shows) you can programatically add the custom behavior before sending the message.
I recommend to anyone dealing with WCF issues, that they first turn on verbose logging and also use fiddler to see what is actually going over the wire.
Instead of creating a WCF service and a client by generated code of WSDL, I must create a WCF service with a given SOAP request.
To help you better understand, I copied the main part of the SOAP request here:
<?xml version="1.0" encoding="windows-1252"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Header>
<HeaderContent xmlns="CustomerNamespace.com">
<CallerId>6274</CompanyId>
<Login>mylogin</Username>
<Password>mypassword</Password>
<ResponsibilityType>3</Responsibility>
<ResponsibilityId>189281728</ResponsibilityId>
</HeaderContent>
</soap:Header>
<soap:Body>
<UpdateNutritionalValues xmlns="CustomerNamespace.com">
<NutritionalValues>
<Product id="195739241">
<Created>2011-11-01</Created>
<Owner>8633</Owner>
.
.
.
So, my service will receive this SOAP request. The embedded NutritionalValues XML is very long and I cannot change anything at the soap request at all because our customer use this request for many other companies as well.
And this is my problem.
How do I map the XML NutritionalValues content to my "DataContract"?
How do I access the Header to authenticate the caller?
When I debug my Service with a handcrafted generated SOAP request with Fiddler I receive a null parameter value
[ServiceContract(Namespace="CustomerNamespace.com")]
public interface IWCFService
{
[OperationContract]
ComplexType UpdateNutritionalValues(NutritionalValues nutVal);
}
[DataContract]
public class NutritionalValues
{
[DataMember(Name="Product")]
public ProductSpecification Product { get; set; }
}
[DataContract]
public class ProductSpecification
{
[DataMember(Name="Created")]
public DateTime Created { get; set; }
[DataMember(Name="Owner")]
public int Owner { get; set; }
}
( I can deserialize the XML manually too ) but i dont know how to access the ( raw? ) XML.
I would be very thankful for any help !
I'm building a WCF router and my client uses Reliable Sessions. In this scenario when the client opens a channel a message is sent (establishing a Reliable Session?). Its contents is as follows:
<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">http://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequence</a:Action>
<a:MessageID>urn:uuid:1758f794-c5d3-4573-b252-7a07344cc257</a:MessageID>
<a:To s:mustUnderstand="1">http://localhost:8010/RouterService</a:To>
</s:Header>
<s:Body>
<CreateSequence xmlns="http://docs.oasis-open.org/ws-rx/wsrm/200702">
<AcksTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</AcksTo>
<Offer>
<Identifier>urn:uuid:64a12658-71d9-4967-88ec-9bb0610f7ecb</Identifier>
<Endpoint>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</Endpoint>
<IncompleteSequenceBehavior>DiscardFollowingFirstGap</IncompleteSequenceBehavior>
</Offer>
</CreateSequence>
</s:Body>
</s:Envelope>
The problem here is that the headers do not contain any information I can use to look up what service to route the message to. In Busatmante's router sample code she gets around this by adding a header to the endpoint:
<client>
<endpoint address="http://localhost:8010/RouterService" binding="ws2007HttpBinding"
bindingConfiguration="wsHttp"
contract="localhost.IMessageManagerService" >
<headers>
<Route xmlns="http://www.thatindigogirl.com/samples/2008/01" >http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>
</headers>
</endpoint>
</client>
When the reliable session is opened the message contains this custom header.
<Route a:IsReferenceParameter="true" xmlns="http://www.thatindigogirl.com/samples/2008/01">http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>
This is great; however, I have a requirement to configure the client programatically. I figured that the ChannelFactory Endpoint would have a Header object to which I could manually add my custom header. Unfortunately it does not. So I did some searching and found some recomendations to extend WCF by implementing an IClientMessageInspector to add my header and adding it as a behavior to my endpoint.
public class ContractNameMessageInspector : IClientMessageInspector {
private const string HEADER_NAME = "ContractName";
private readonly string _ContractName;
public ContractNameMessageInspector(string contractName) {
_ContractName = contractName;
}
#region IClientMessageInspector Members
public void AfterReceiveReply(ref Message reply, object correlationState) { }
public object BeforeSendRequest(ref Message request, IClientChannel channel) {
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject)) {
httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
if (httpRequestMessage != null && string.IsNullOrEmpty(httpRequestMessage.Headers[HEADER_NAME])) {
httpRequestMessage.Headers[HEADER_NAME] = this._ContractName;
}
}
else {
httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add(HEADER_NAME, this._ContractName);
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
}
return null;
}
#endregion
}
So when my client makes a service call the message contains the custom header but the message establishing the Reliable Sessions still does not.
So my question is; how do I add a custom header to the Endpoint programatically in such a way that the reliable session message contains it?
Many Thanks
Figured it out. Although there is no property or method to add a header to an EndpointAddress there is an optional parameter on the constructor.
_Binding = bindingFactory.GetBinding(serviceUri, typeof(T));
AddressHeader header = AddressHeader.CreateAddressHeader("ContractName", "NameSpace", typeof (T).ToString());
_Address = new EndpointAddress(serviceUri, header);
_ChannelFactory = new ChannelFactory<T>(_Binding, _Address);
Now when I receive the message establishing the reliable session it actually does contain my custom header. This kinda makes sense as the message inspector most likely only operates on dispatched messages while the message establishing the reliable session is generated by lower level WCF code.
This works for me
public TResult Invoke<TResult>(Func<TClient, TResult> func,MessageHeader header)
{
TClient client = default(TClient);
var sw = new Stopwatch();
try
{
sw.Start();
using (client = _channelFactory.CreateChannel())
{
using (OperationContextScope contextScope = new OperationContextScope(client))
{
OperationContext.Current.OutgoingMessageHeaders.Add(header);
return func.Invoke(client);
}
}
}
finally
{
CloseConnection(client);
Instrument(this.GetType().Name, sw);
}
}
To programmatically add address headers see MSDN's Address Headers where one can programmatically add a header such as:
var cl = new MyWCFClientContext();
var eab = new EndpointAddressBuilder(cl.Endpoint.Address);
eab.Headers.Add( AddressHeader.CreateAddressHeader("ClientIdentification", // Header Key
string.Empty, // Namespace
"JabberwockyClient")); // Header Value
cl.Endpoint.Address = eab.ToEndpointAddress();
Can anyone point me to an example how to post a SOAP Request to a WCF Service and return a SOAP Response? Basically a Travel client sends a SOAP request with search parameters and the WCF Service checks within the database and then sends the appropriate holidays.
I keep getting this error, with the method I have used: "The remote server returned an error: (400) Bad Request"
The error you got is because the server does not understand the HTTP request.
It could be the binding you configured or the service proxy is incorrect at client level.
Or the service you defined expects HTTP GET rather than HTTP POST. Sometimes the add service reference may not generate correct HTTP verb for some [WebGet] attributed operations. You may need to add [WebGet] for the operation at client side manually.
Either have a look at SoapUI, or locate the WcfTestClient buried deep in your Visual Studio folders (C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE).
Both can connect to a WCF service and send/receive SOAP messages.
Or create your own little client, using svcutil.exe:
svcutil.exe (service URL)
will create a little *.cs file and a *.config file for you, which you can then use to call the service.
Marc
You haven't given many details as to how far along you are with the service, so it's hard to say.
If this is literally the first hit to the service, this error could occur if WCF has not been registered properly with IIS. Specifically the .svc extension needs to be mapped to the ASP.NET ISAPI module.
thanks for taking the time out to answer this.
The service works fine, if a client creates a reference to my WCF Service and makes a method call, the appropriate response is sent.
I forgot to add, that my client is sends a HTTP Post Request to my WCF Service.
The appropriate response is then created and returned to the Client.
I can read the HTTP Request, however when i try and access the HTTP response, i get error -"The remote server returned an error: (400) Bad Request"
The error happens when the code reaches this line:
// Get the response.
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
See code below:
private void CreateMessage()
{
// Create a request using a URL that can receive a post.
WebRequest request = WebRequest.Create("http://www.XXXX.com/Feeds");
string postData = "<airport>Heathrow</airport>";
// user function
request.Method = "POST";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/soap+xml; charset=utf-8";
request.ContentLength = byteArray.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
// Get the response.
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
// Display the status.
HttpContext.Current.Response.Write(((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
HttpContext.Current.Response.Write(responseFromServer);
// Clean up the streams.
reader.Close();
dataStream.Close();
response.Close();
}
regards
Kojo
Note
The recommended way of accessing WCF Service from other .NET application is by using the "Connected Services" reference. Below I describe how you can create and send SOAP requests in a more manual (and not recommended for production code) manner.
In short
You need:
Content-Type: text/xml; charset=utf-8 header
SOAPAction: http://tempuri.org/YourServiceClass/YourAction header
Request content wrapped in SOAP envelope.
Longer version (example)
Lets take a WCF Service Application scaffolding as an example.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
}
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
Using Wireshark, I found out that the requests made the default way (connected service reference) contain Content-Type: text/xml; charset=utf-8 and SOAPAction: http://tempuri.org/IService1/GetData headers and following SOAP envelope:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetData xmlns="http://tempuri.org/"> <!-- Action name -->
<value>123</value> <!-- Parameters -->
</GetData>
</s:Body>
</s:Envelope>
Using Insomnia, I tested that it's all we need in order to make the request pass successfully, so now just need to port it to the C#:
// netcoreapp3.1
static async Task<string> SendHttpRequest(string serviceUrl, int value)
{
// Example params:
// serviceUrl: "http://localhost:53045/Service1.svc"
// value: 123
using var client = new HttpClient();
var message = new HttpRequestMessage(HttpMethod.Post, serviceUrl);
message.Headers.Add("SOAPAction", "http://tempuri.org/IService1/GetData"); // url might need to be wrapped in ""
var requestContent = #$"
<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
<s:Body>
<GetData xmlns=""http://tempuri.org/"">
<value>{value}</value>
</GetData>
</s:Body>
</s:Envelope>
";
message.Content = new StringContent(requestContent, System.Text.Encoding.UTF8, "text/xml");
var response = await client.SendAsync(message);
if (!response.IsSuccessStatusCode)
throw new Exception("Request failed.");
var responseContent = await response.Content.ReadAsStringAsync();
/*
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetDataResponse xmlns="http://tempuri.org/">
<GetDataResult>You entered: {value}</GetDataResult>
</GetDataResponse>
</s:Body>
</s:Envelope>
*/
// Just a really ugly regex
var regex = new Regex(#"(<GetDataResult>)(.*)(<\/GetDataResult>)");
var responseValue = regex.Match(responseContent).Groups[2].Value;
return responseValue;
}
You can ofc. use WebClient instead of HttpClient if preferred.