8

Here is what the header is supposed to look like

<soap:Header>
   <AuthenticationHeader>
     <UserName>string</UserName>
     <Password>string</Password>
   </AuthenticationHeader>
 </soap:Header>

Here is what I've tried:

string username = "TheUserName";
string password = "ThePassword";

HttpRequestMessage requestMessage = new HttpRequestMessage(method, uri);
requestMessage.Headers.Add("UserName", username);
requestMessage.Headers.Add("Password", password);

Maybe I have to somehow set the authorization header?

requestMessage.Headers.Authorization = ??

I feel like somehow I have to "build" that AuthenticationHeader element but I'm not sure how to do that. Any suggestions?

Edit: Full SOAP Envelope

?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:Header>
    <AuthenticationHeader xmlns="http://www.test.com/testing/Security">
      <UserName>string</UserName>
      <Password>string</Password>
    </AuthenticationHeader>
  </soap:Header>
  <soap:Body>
    <GetMeSomething xmlns="http://www.test.com/testing/WorkFileCatalog">
      <Param1>string</Param1>
      <Param2>string</Param2>
      <XMLRetMess>string</XMLRetMess>
    </GetMeSomething>
  </soap:Body>
</soap:Envelope>
Pittfall
  • 2,751
  • 6
  • 32
  • 61
  • 1
    the normal and general way is to attach the user and pwd to the object that is used to send the request like `yourObject.Credentials = new System.Net.NetworkCredential(username, password);` did you tried that? – balexandre Jul 17 '17 at 12:45
  • I'm not sure what you mean by `yourObject`. I'm trying to use `HttpClient`. I actually created a service reference which simplifies things a lot but I should be able to use `HttpClient` instead and just use the service reference for just the request and response objects. I'm just having trouble with syntax as I don't know how all of of these objects translate to markup. – Pittfall Jul 17 '17 at 13:00
  • if you're using `HttpClient` as the request obejct, then simply [attach the credentials like this](https://stackoverflow.com/a/15034995/28004). – balexandre Jul 17 '17 at 13:16
  • if that does not do the trick, you really need to do the send in 2 steps, send the authentication first, wait for reply and then send the your normal request (normally with a header with the response you got from the authentication)... – balexandre Jul 17 '17 at 13:27
  • 2
    @Pittfall you do know that the SOAP header is part of the message envelope which is sent as port of the body of the request and has no relation to the HTTP request Authorization header? – Nkosi Jul 17 '17 at 19:57
  • @Pittfall you will need to clarify what it is you are actually trying to do as the SOAP header presented is not part of a HTTP Authorization header. What is the scenario being implemented here? – Nkosi Jul 17 '17 at 20:06
  • I'm with @Nkosi The soap headers are independent of the http headers. Whatever solution you use needs to take that into account. – ste-fu Jul 19 '17 at 19:58
  • @Pittfall are you going to clarify this question before the bounty ends or are those trying to help you going to have to just guess you intentions and get down-voted for it? – Nkosi Jul 21 '17 at 13:50
  • You want to fill Authentication header with property Headers.Authorization. I suppose noone will tell you how to do it – ASpirin Jul 24 '17 at 12:35
  • @ASpirin Because what was being asked is not technically possible, as stated by Nkosi – Chawin Jul 24 '17 at 12:54
  • @NKoski I'm sorry about not replying, I started the bounty at the wrong time as I was away all week. I actually did not realize that the credentials are not part of the httpheader. I don't know why I missed that but yes, I did miss it. I will re investigate my solution but essentially I was trying to figure out how to pass that SOAP header to my request using `HttpClient`. I did not want to manually build that header and I wanted to use the objects available in the framework. – Pittfall Jul 24 '17 at 13:09
  • @Pittfall Essentially you can, but it would involve injecting the header into the SOAP message itself. Can you show a sample of the envelope? There are a few options available. – Nkosi Jul 24 '17 at 13:43
  • @Nkosi, I've put the full Envelope in the question – Pittfall Jul 24 '17 at 15:01
  • @Pittfall Ok so just finish first draft. Have a question though. The sample soap you provided already has the header present. Is that how you are going to get the xml before sending it or do you have to add it. I'll post what I have so far based on what you have provided and you will let me know – Nkosi Jul 24 '17 at 16:55
  • @Nkosi That soap envelope is the request. I guess to be a little bit more clear, I was sort of wondering if I can somehow add that header without having to manually parse it and replace. If that's the only solution, then I will accept that but I called out to another service in the past with no header, just a soap body and I did not have to manually put in any xml. I filled my requestMessage.Content with a FormUrlEncodedContent object and away it went. I can further detail that example if needed. I thought I could somehow do the same for the header. – Pittfall Jul 24 '17 at 18:17

2 Answers2

1

Given the provided OP, the following Unit Test was done as a proof of concept of how you can populate the header message header and create a request.

[TestClass]
public class SOAP_UnitTests {
    private HttpMethod method;
    private string uri;
    private string action;

    [TestMethod]
    public void _Add_SOAP_Auth_Header_Details_With_HttpRequestMessage() {
        string username = "TheUserName";
        string password = "ThePassword";

        var xml = ConstructSoapEnvelope();
        var doc = XDocument.Parse(xml);
        var authHeader = doc.Descendants("{http://www.test.com/testing/Security}AuthenticationHeader").FirstOrDefault();
        if (authHeader != null) {
            authHeader.Element(authHeader.GetDefaultNamespace() + "UserName").Value = username;
            authHeader.Element(authHeader.GetDefaultNamespace() + "Password").Value = password;
        }
        string envelope = doc.ToString();

        var request = CreateRequest(method, uri, action, doc);
        request.Content = new StringContent(envelope, Encoding.UTF8, "text/xml");

        //request is now ready to be sent via HttpClient
        //client.SendAsync(request);
    }

    private static HttpRequestMessage CreateRequest(HttpMethod method, string url, string action, XDocument soapEnvelopeXml) {
        var request = new HttpRequestMessage(method: method, requestUri: url);
        request.Headers.Add("SOAPAction", action);
        request.Headers.Add("ContentType", "text/xml;charset=\"utf-8\"");
        request.Headers.Add("Accept", "text/xml");
        request.Content = new StringContent(soapEnvelopeXml.ToString(), Encoding.UTF8, "text/xml"); ;
        return request;
    }

    private string ConstructSoapEnvelope() {
        var message = @"<?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:Header>
    <AuthenticationHeader xmlns='http://www.test.com/testing/Security'>
      <UserName>string</UserName>
      <Password>string</Password>
    </AuthenticationHeader>
  </soap:Header>
  <soap:Body>
    <GetMeSomething xmlns='http://www.test.com/testing/WorkFileCatalog'>
      <Param1>string</Param1>
      <Param2>string</Param2>
      <XMLRetMess>string</XMLRetMess>
    </GetMeSomething>
  </soap:Body>
</soap:Envelope>
";
        return message;
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
-1

If you are using HttpClient to POST a request, then you should build the full XML request.

In other words, you would build the exact Soap XML including all the elements

string requestXml = your actual full soap xml
string result = HttpClient.Post ( your actual xml )
Ali
  • 3,373
  • 5
  • 42
  • 54
Subbu
  • 2,130
  • 1
  • 19
  • 28
  • Please remove this answer, I don't mean to offend you but this is a hack and not what I was looking for. It may work but I want to use the objects provided in .NET. Doing anything like this will fail as soon as there is a change in the protocol. – Pittfall Jul 17 '17 at 13:53
  • Hmmm... this is a very valid approach for really simple Soap requests. We use this and are happy with it so far. IMHO, it all depends on the context. – Subbu Jul 17 '17 at 14:14
  • I didn't mean it won't work, I just meant that it's not a nice solution. Let me give you an example. Imagine using `string s = ""` vs `string s = string.Empty`. Both valid solutions but if the value of an empty string changes from `""` to `"|"`, `string.Empty` will be updated. Now this example is probably based on something that will never change but I hope you get the idea. – Pittfall Jul 17 '17 at 14:27