2

I am calling a 3rd party SOAP service (a Magento webstore) in ASP.NET MVC4. When importing the web service reference, all of the service methods are automatically implemented by Visual Studio, eg the login soap method is implemented as

    public string login(string username, string apiKey) {
        object[] results = this.Invoke("login", new object[] {
                    username,
                    apiKey});
        return ((string)(results[0]));
    }

But when I call this method, this.Invoke sends a POST with this user-agent header automagically added:

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; 
            MS Web Services Client Protocol 4.0.30319.18444)

This header tells the 3rd party that the user agent is IE6. And many sites automatically block IE6 with a message saying something to the effect of "We do not support IE6. Go get a real browser and try again"

So the soap call breaks but only because the 3rd party site thinks we are using IE6, not because there is anything wrong with the soap call. If we could change this header to mimic the UA string of a modern web browser then this problem would not exist.

So how then do you change the UA string used by SoapHttpClientProtocol method calls? It all happens inside of the this.Invoke method, which is part of the .NET core.

EDIT:

The object this in the above autogenerated code above is a subclass of SoapHttpClientProtocol, so yes I could just manually write the user agent in there myself:

    public string login(string username, string apiKey) {
        this.UserAgent = "something, anything except for IE6";
        object[] results = this.Invoke("login", new object[] {
                    username,
                    apiKey});
        return ((string)(results[0]));
    }

BUT, this is autogenerated code and will be overwritten anytime the 3rd party updates their service (for Magento it is quite frequently), and I would have to manually add it to every single autogenerated function (a lot of them). So it's not practical to just write this.UserAgent = "not IE6" here, it needs to be a more useful solution.

JK.
  • 21,477
  • 35
  • 135
  • 214
  • 1
    I may be overlooking something but SoapHttpClientProtocol has a Read/Write UserAgent property: http://msdn.microsoft.com/en-US/library/system.web.services.protocols.httpwebclientprotocol.useragent.aspx – Simon Mourier Jun 03 '14 at 04:45
  • @SimonMourier sorry, I didn't explain clearly why I didn't do that. Question updated now – JK. Jun 03 '14 at 04:52
  • Why can't you change the property when you instantiate the WebService generated class, w/o changing anything to the generated code? You can also derive from the class. – Simon Mourier Jun 03 '14 at 05:04
  • @SimonMourier I also want to avoid having to specify the UA string everytime I instantiate (DRY etc), but deriving is a good idea and could work - the subclass can have the UA written into the constructor, and I can change all the instantiations to instantiate the sub class instead. – JK. Jun 03 '14 at 05:19
  • The last solution is to hack the HttpWebClientProtocol using reflection and change the default user agent string before any web service instanciation. This is not supported of course: `typeof(HttpWebClientProtocol).GetField("UserAgentDefault", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, "My user agent");` – Simon Mourier Jun 03 '14 at 05:34
  • @SimonMourier I've implemented the derived class and it looks good, so if you want to convert comment to answer it will most likely be accepted – JK. Jun 08 '14 at 00:04

2 Answers2

2

The generated Web Service reference class derives itself from SoapHttpClientProtocol, something like this:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.0.30319.18408")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="MyGeneratedWebServiceSoap", Namespace="http://www.killroy.com/webservices/")]
public partial class MyGeneratedWebService : System.Web.Services.Protocols.SoapHttpClientProtocol
{
    ...
}

SoapHttpClientProtocol has a read/write UserAgent property, so what you could do is derive from this class again and customize the user agent like this (this way you can automatically replace all instance creations of the original class by the new one):

public class SuperWs: MyGeneratedWebService
{
    public SuperWs()
    {
        UserAgent = "Mozilla/5.0 (Killroy was here)";
    }
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
1

is the autogenerated class a partial class?

When it is a partial class then you should create an own extention to the generated class like "myWebservice_partial.cs", rename the class to:

  • public partial class "GENERATEDCLASSNAME"{}

    and define/ override the constructor. within this you can set your UserAgent. This is updatesave.

This code is untested and written from my brain. I don`t know now if you have to innerhit from the SoapHttpClientProtocol (See Comment)

E.G.

FileName: WsClass_partial.cs

public partial class WsClass /* :SoapHttpClientProtocol */ {
  public WsClass(string useragent):this(){
     this.UserAgent = useragent;
  }
}
Community
  • 1
  • 1
Bjego
  • 665
  • 5
  • 14