How to consume SOAP service with custom header - c#

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.

Related

How can I get/create the request structure from Soap service?

I have the wsdl of a web soap service and I have added it as a service reference with visual studio. I have notice that it doesn't have a request class for a method which I need to use. The reason I need it is because I have to store that request as xml and I have always used the request class to serialize. Is there a way to generate that request xml?
Simple representation:
var client = new SoapServiceCliente();
var request = new RequestClass(); <-- This is what soap service is missing.
var response = new ResponseClass();
response = client.theMethod(request);
Before calling the method I serialize the request to xml and after getting the response I serialize it too.

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.

Inject header into WCF outbound message

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
}

Getting MIME type from Web Service Response header

I invoke a web service provided by an external partner company. The web service returns files (.pdf, .dox, .png, ...) as a byte array.
If I would need to get the Header information (in detail I am interested in the content-type data) from the code, how can I get this information?
On our side, we are using VS 2010 and C# as language.
Here the code:
var client = new PublicService();
wsRequest request = new wsRequest();
var docInfo = new documentInfo();
docInfo.documentId = HSdocumentID;
docInfo.position = 1;
request.documentInfos = { docInfo };
byte[] doc = client.deliver(deliverRequest); //returns the file as byte array
The RESPONSE header would look like:
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body><ns2:deliverResponse xmlns:ns2="http://www.dalle.com/webservices/record/2012a">
<return>
<Include xmlns="http://www.w3.org/2004/08/xop/include"
href="cid:d3a#example.jaxws.sun.com"/>
</return></ns2:deliverResponse></S:Body></S:Envelope>
Content-Id: <d3a#example.jaxws.sun.com>
Content-Transfer-Encoding: binary
Content-Type: application/pdf <-- THIS IS THE INFO I NEED TO GET
Check out if there are SOAP headers on the Web Method call that answer your question
On the web method I do not have any properties/attributes that refer to the header. Is there a general way to get the Response header or is the web service that should provide features to get it?
(I provided an answer, rather than a comment, due to the code to be copied)

Categories