Inject header into WCF outbound message - c#

I have a WCF service built on the classes created from a customer supplied WSDL. Unfortunately this WSDL did not contain the required message header. The client will not be supplying a new WSDL including the header. I do have an xsd file describing the header.
I also have a sample header and know which fields I need to populate.
How can I take this supplied header XML and inject it into an outbound WCF method call?
I want to call my service method as I currently do, but I also want the new header structure to form part of the outbound message.
Thanks in advance.
Any and all help will be greatly appreciated.
Here is an example of the message structure:
I need to add the entire header structure. All that the WSDL contained was the body.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<glob:requestHeader xmlns:glob="http://....">
<timestamp>2013-11-14T05:17:41.793+02:00</timestamp>
<traceMessageId>GUID</traceMessageId>
<enterpriseTraceUUId>GUID</enterpriseTraceUUId>
<contentType>TEXT/XML</contentType>
<sender>
<senderId>SENDER</senderId>
<sourceSystem>001</sourceSystem>
<sourceApplication>001</sourceApplication>
<applicationSessionId>ABC</applicationSessionId>
<sourceLocation>100</sourceLocation>
</sender>
<interfaceName/>
<version>1111</version>
</glob:requestHeader>
</s:Header>
<s:Body xmlns:xsi="http://.../XMLSchema-instance" xmlns:xsd="http://.../XMLSchema">
<UserData xmlns="http://.../Base">
<IdField>1005687</IdField>
<UserInfo>
<UserType>1</UserType>
<UserStatus>Y</UserStatus>
</UserInfo>
</UserData>
</s:Body>
</s:Envelope>

I used this, for instance, to add "User-Agent" to the header of my outgoing messages, but I think you could adapt it to your own needs:
private void AddCustomHeader(System.ServiceModel.OperationContextScope scope)
{
dynamic reqProp = new System.ServiceModel.Channels.HttpRequestMessageProperty();
reqProp.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT; blah; blah; blah)");
System.ServiceModel.OperationContext.Current.OutgoingMessageProperties(System.ServiceModel.Channels.HttpRequestMessageProperty.Name) = reqProp;
}
I call this function above from the the constructor of the client-side program I use to call the host.
AddCustomHeader(new System.ServiceModel.OperationContextScope(base.InnerChannel));
Probably the most important thing to notice is that it's adding this header variable to OutgoingMessageProperties of the "Current" OperationContext used by my client.

have you tried this? Also taken from here: How to add a custom HTTP header to every WCF call?
using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel))
{
MessageHeader<string> header = new MessageHeader<string>("secret message");
var untyped = header.GetUntypedHeader("Identity", "http://www.my-website.com");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
// now make the WCF call within this using block
}

Related

How to consume SOAP service with custom header

I need to consume a SOAP service from a vendor. I had created a proxy service with the WSDL in visual studio, instantiated the client class, called the action method, and got the response. Everything works fine until the vendor asks for an access token in the soap envelop header. I am able to get the access token from them on another service call but how do I add it to the soap request header?
Here is the structure of the header from the vendor:
<SOAP:Header>
<SOAP-SEC:Security SOAP:mustUnderstand="1">
<wsse:SecuredKey ValueType="..." EncodingType="wsse:Base64Binary">
{ACCESS TOKEN}
</wsse:SecuredKey>
</SOAP-Sec:Security>
</SOAP:Header>
<SOAP:Body/>
</SOAP:Envelop>
By far the easiest way to do this is by using a string replace in a template. Store the message as a resource in your project (e.g. in Resources.resx or save as file and set set build action to embedded resource). The template looks like this:
<SOAP:Header>
<SOAP-SEC:Security SOAP:mustUnderstand="1">
<wsse:SecuredKey ValueType="..." EncodingType="wsse:Base64Binary">
{ACCESS TOKEN}
</wsse:SecuredKey>
</SOAP-Sec:Security>
</SOAP:Header>
<SOAP:Body/>
</SOAP:Envelop>
Load the template as string from your resource, then call the webservice to obtain an access token and do and replace {ACCESS TOKEN} with the actual access token. You can now send the soap message using e.g. System.Net.Http.HttpClient or System.Net.WebClient.
Example using WebClient
using (var client = new WebClient())
{
var result = client.UploadString("http://your.target/endpoint", yourXDocument.ToString(SaveOptions.DisableFormatting));
return XDocument.Parse(result);
}
The SaveOptions.DisableFormatting doesn't try to pretty print the XDocument which can be important when using a signed xml document with WS-Security. Not sure if that applies in your case.

How Do I Change XML Prefix For WCF Client Request c#?

Due to requirements from a web service that I have no control over, I need to change my ns prefixes from the defaults of "s" and "h". The service provider has provided a .wsdl file for my service reference, however, they do not appear to have an online wsdl for metadata exchange. I have emailed them and they confirmed that the prefixes have to be specific values (right or wrong on their part, I have to make this work).
Can someone please tell me the easiest way to change these values? Is there something I can modify in Reference.cs? Do I have to implement some sort of message formatting logic into my requests to change the values just prior to sending out the request?
Every element in the XML has to have a prefix of a specific value (2 values total), or the WS will not accept the request and simply returns HTML in the response. When I copy the request captured in Fiddler, over to SOAP UI and update the prefixes to what they require, the request works fine when executed from Soap UI. I'm simply not finding any simple solutions to this seemingly simple problem for c# VS 2017 development platform.
Also worth noting, when I load the .wsdl file into Soap UI, Soap UI generates all of the ws operations and requests with the correct, desired prefixes that I need in my c# app. How does Soap UI know the exact prefixes to generate in the requests?
I need to modify this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:FooListHeader xmlns:h="http://foo.foo.com/Hello" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<h:FooList>
<h:FooStatement>
<h:Title>Foo</h:Title>
<h:Value>123</h:Value>
</h:FooStatement>
</h:FooList>
</h:FooListHeader>
</s:Header>
<s:Body>
<GetFooRequestType xmlns="http://foo.foo.com/Hello">
<MessageRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ConFooRequest/>
</MessageRequest>
</GetFooRequestType>
</s:Body>
</s:Envelope>
To something like this:
<soapenv:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:foo="http://foo.foo.com/Hello">
<soapenv:Header>
<foo:FooListHeader xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<foo:FooList>
<foo:FooStatement>
<foo:Title>Foo</foo:Title>
<foo:Value>123</foo:Value>
</foo:FooStatement>
</foo:FooList>
</foo:FooListHeader>
</soapenv:Header>
<soapenv:Body>
<foo:GetFooRequestType xmlns="http://foo.foo.com/Hello">
<foo:MessageRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<foo:ConFooRequest/>
</foo:MessageRequest>
</foo:GetFooRequestType>
</soapenv:Body>
</soapenv:Envelope>
Example c# code:
public GetYaddaYaddaResponseType CheckConnection()
{
Yadda client = new Yadda();
var msgRequest = new GetYaddaYaddaResponseType { ConnectionConfirmationRequest = new ConfirmConnectRequestType() };
List<Statement> statementList = new List<Statement>
{
new Statement { Name = "Yo", Value = "123" },
new Statement { Name = "Hey", Value = "123" },
};
var header = new StatementHeader
{
StatementList = statementList.ToArray()
};
var response = client.GetYaddaYaddaMessage(ref header, msgRequest);
return response;
}

How to serialize an object into string\xml with its headers

I'm using a third side web service client (created by using the "Add service reference") in order to retrieve some data.
After filling the web service objects with proper data we need to add some data to the headers (encrypted password and some other predefined data)
Then, we are serializing every request sent to the web service, using the standard .net XmlSerializer.
However, in the result of the serialization I don't see the headers of the request. I've searched for a long time and couldn't find any way to "print" them as well.
Here is some example code:
Ibooking proxy = new BookingManager();
/* Init proxy Data...*/
GetAvailabilityRequest request = new GetAvailabilityRequest();
/*Fill more data on the request...*/
GetAvailabilityResponse response = proxy.GetAvailability(request); //Send request to the web service
var xmlString2 = response.Serialize(); //only body, no headers in the XML
/* Extension class to Serialize any object */
public static class ExtensionUtil
{
public static string Serialize<T>(this T value)
{
try
{
XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using (var writer = XmlWriter.Create(stringWriter))
{
xmlserializer.Serialize(writer, value);
return stringWriter.ToString();
}
}
catch (Exception ex)
{
throw new Exception("An error occurred", ex);
}
}
}
I've excluded the code that adds more data to the request since it's long and complicated (need to implement IEndpointBehavior and IClientMessageInspector to "catch" the request before we send it) - but currently as a workaround I put a BreakPoint on the Message object and convert it into string using Visual Studio. In this way I do see the headers but obviously this is bad practice since I want it to be automated in the serialization.
I would like to see an example of how you are adding these headers.
In most web services the message body is the part that is serialized into XML or JSON - the headers are not.
You may be able to inspect the service call by using Fiddler and a proxy implemented by a small change in your web.config as described in this article: http://weblog.west-wind.com/posts/2008/Mar/14/Debugging-Http-or-Web-Services-Calls-from-ASPNET-with-Fiddler.
The short version of this is to add the following to your web.config or app.config:
<system.net>
<defaultProxy>
<proxy proxyaddress="http://127.0.0.1:8888" />
</defaultProxy>
</system.net>
Download and run Fiddler while calling the service and you should see and be able to inspect the call in Fiddler.
If you want to inspect and/or modify the headers within your code base could look into implementing IClientMessageInspector or IDispatchMessageInspector. Here are a couple articles on the topic:
https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.iclientmessageinspector(v=vs.100).aspx
http://weblogs.asp.net/paolopia/writing-a-wcf-message-inspector
Here is an implementation I did. I didn't need access the headers, but rather to modify the xml namespaces created by the service client, but it should give you an idea on how to do the implementation: How can I create custom XML namespace attributes when consuming a legacy SOAP service?
OperationContext is your friend here. Use an OperationContextScope to wrap the call to the service, then use OperationContext.Current to get at all the hidden goodies you need.
https://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontextscope(v=vs.110).aspx
Note that you'll need to know the specific types of the headers you want to get at, and I've had some trouble getting at the values, rather than just the names, of headers if they're not marked as serializable when using XmlSerializer

accessing php soapservice from C#

well i wanted to make a simple webservice that searches the db and return the data i know i can do it with mysql connector but this is just to learn how to use soaps here is the code for php soap server
require_once ('lib/nusoap.php');
$namespace = "http://localhost/webservice/index.php?wsdl";
$server = new soap_server();
$server->configureWSDL("DBQuery");
$server->wsdl->schemaTargetNamespace = $namespace;
$server->register(
'QueryMsg',
array('name'=>'xsd:string'),
array('return'=>'xsd:string'),
$namespace,
false,
'rpc',
'encoded',
'returns data from database');
function QueryMsg($query)
{
$con=mysqli_connect('localhost','root','','webserivce');
if (mysqli_connect_errno()) {
return "Failed to connect to MySQL: " . mysqli_connect_error();
}
if(!isset($query) or strpos(strtolower($query),'select')<=-1)
{
return "invalid order";
}
else
{
mysqli_real_escape_string($con,$query);
$result = mysqli_query($con,$query);
while($row = mysqli_fetch_array($result)) {
$data[] = $row;}
return json_encode($data);
}
}
// create HTTP listener
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
exit();
?>
it works when i try calling it from a php soap client but when i try adding this http:// localhost /webservice/index.php in visual studio as service refernce to consume it from C# application i get an error here it is
The HTML document does not contain Web service discovery information.
Metadata contains a reference that cannot be resolved: 'http://localhost/webservice/index.php'.
The content type text/xml; charset=ISO-8859-1 of the response message does not match the content type of the binding (application/soap+xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 700 bytes of the response were: '<?xml version="1.0" encoding="ISO-8859-1"?><SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body><SOAP-ENV:Fault><faultcode xsi:type="xsd:string">SOAP-ENV:Client</faultcode><faultactor xsi:type="xsd:string"></faultactor><faultstring xsi:type="xsd:string">Operation &apos;&apos; is not defined in the WSDL for this service</faultstring><detail xsi:type="xsd:string"></detail></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>'.
The remote server returned an error: (500) Internal Server Error.
If the service is defined in the current solution, try building the solution and adding the service reference again.
solved : well it was easy actually there is two ways either use WCF and change encoding to ISO-8859-1
or change encoding of the web service itself by adding this line $server->soap_defencoding = 'UTF-8'; after creating the soap server
I would try adding the service WSDL with a tool like SOAP U.I. and see what kind of errors you get back from that. It's a little more agnostic than adding a web reference with C#, and might disclose more details about why at the client level you can't consume this.
I'm happy to help you troubleshoot this with a little more information. Are you running this service on the same machine where you're running the client from? If it's complaining about being unable to correlate the file http://localhost/webservice/index.php to something I wonder if the discovery process is trying to evaluate a file that can't be found. I.E. an import operation in your source WSDL that points to a URL the client can't resolve.

How do I get to explore WCF methods with a browser?

I've got a net.tcp service, which I want to make accessible to other platforms (specifically, PHP). For this, I'm using http binding.
I'm creating a http endpoint with:
ServiceHost svh = new ServiceHost(typeof(MyService));
var httpLocation = "http://" + address + ":4041";
svh.AddServiceEndpoint(typeof(IMyService), new WebHttpBinding(WebHttpSecurityMode.None),
httpLocation);
svh.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
httpLocation + "/mex"
);
svh.Open();
Now, when I'm trying to explore the service via browser by going to http://localhost:4041, I'm getting:
<Fault xmlns="http://schemas.microsoft.com/ws/2005/05/envelope/none">
<Code>
<Value>Sender</Value>
<Subcode>
<Value xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:ActionNotSupported</Value>
</Subcode>
</Code>
<Reason>
<Text xml:lang="en-US">
The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
</Text>
</Reason>
What am I doing wrong?
When browsing, make sure to include the full path to the .svc or .asmx along with a trailing ?wsdl.
localhost:4041/ServiceVirtualDirectory/Service.svc?wsdl
Have you tried accessing http://localhost:4041/MyService.svc?wsdl?
Not sure if your service is REST-ful by design and that's why you used WebHttpBinding.
If your service is actually REST-ful, its format would be
http://localhost:4041/MyService.svc/MyMethod/MyData and you should see the results on the browser. Based on this ASP.NET thread, you would need to attach a WebHttpBehavior.
If your serivce isn't REST-ful, you should consider BasicHttpBinding (uses regular SOAP messages) or its cousins based on your requirements (see this SO thread).
Hope this helps.

Categories