Ok, so I worked on this a bit and tried to reproduce the problem on my side. I was able to reproduce the problem and find a solution for it. However, I am not sure how applicable this is in your case, since it depends on the interaction with the server team that controls the load balancer. Here are the results.
Looking at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html , you notice the following addition in the explanation for HTTP status codes 302 and 303.
302 Found
Note: RFC 1945 and RFC 2068 specify that the client is not allowed to change the method on the redirected request. However, most existing user agent implementations treat 302 as if it were a 303 response, performing a GET on the Location field-value regardless of the original request method. The status codes 303 and 307 have been added for servers that wish to make unambiguously clear which kind of reaction is expected of the client.
303 See others
Note: Many pre-HTTP/1.1 user agents do not understand the 303 status. When interoperability with such clients is a concern, the 302 status code may be used instead, since most user agents react to a 302 response as described here for 303.
Next, by looking at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes , you will see the following explanation for HTTP status codes 302, 303, and 307.
302 Found: This is an example of industry practice that is contrary to the standard. The HTTP / 1.0 specification (RFC 1945) required the client to perform a temporary redirect (the original descriptive phrase was "Moved Temporarily") , but popular browsers implemented 302 with 303 functionality. See the section "Other". Therefore, HTTP / 1.1 added status codes 303 and 307 to distinguish between the two behaviors. However, some web applications and frameworks use a status code of 302, as if it were 303.
303 See Other (with HTTP / 1.1): The response to the request can be found in another URI using the GET method. When received in response to a POST (or PUT / DELETE), it should be assumed that the server received the data, and the redirect should be issued in a separate GET message. Here is the main thread in normal Client / Server interaction
307 Temporary redirect (with HTTP / 1.1): In this case, the request must be repeated with a different URI; however, future requests should still use the original URI. In contrast to how historically 302 was implemented, the request method cannot be changed when the original request is re-issued. For example, a POST request should be repeated using another POST request.
Thus, according to this, we can explain the behavior of the WCF call, which sends a GET request without s: Envelope when redirecting 302. This will undoubtedly fail on the client side.
The easiest way to fix this is to return the server to a 307 temporary redirect instead of the status code 302 of the response found. Here you need the help of Server Team, which manages the forwarding rules on the load balancer. I tested it locally, and client code consuming a service with Service Reference can make a call even with a 307 temporary redirect without problems.
In fact, you can test all of this with the solution I uploaded to Github here . I updated this to illustrate using a service link instead of the proxy class created by wsdl to use the asmx service.
However, if a change from 302 found in 307 temporary redirects is not possible in your environment, then I would suggest using either Solution 1 (which should not have a problem, whether it is 302 or 307 in the answer) or using my original answer , which would allow this by directly contacting the service at the correct URL based on the settings in the configuration file. Hope this helps!
Solution 1
If you do not have access to the configuration files during production, or if you simply do not want to use multiple URLs in the configuration file, you can use this following approach. Link to Github repository containing sample solution Click here
Basically, if you notice the auto file created by wsdl.exe, you will notice that the service proxy class is obtained from System.Web.Services.Protocols.SoapHttpClientProtocol . This class has a protected System.Net.WebRequest GetWebRequest(Uri uri) method, which you can override. Here you can add a validation method to determine if temporary redirect 302 is the result of the HttpWebRequest.GetResponse() method. If so, you can set Url for the new Url returned in the Response Location header, as shown below.
this.Url = new Uri(uri, response.Headers["Location"]).ToString();
So, create a class called SoapHttpClientProtocolWithRedirect as follows.
public class SoapHttpClientProtocolWithRedirect : System.Web.Services.Protocols.SoapHttpClientProtocol { protected override System.Net.WebRequest GetWebRequest(Uri uri) { if (!_redirectFixed) { FixRedirect(new Uri(this.Url)); _redirectFixed = true; return base.GetWebRequest(new Uri(this.Url)); } return base.GetWebRequest(uri); } private bool _redirectFixed = false; private void FixRedirect(Uri uri) { var request = (HttpWebRequest)WebRequest.Create(uri); request.CookieContainer = new CookieContainer(); request.AllowAutoRedirect = false; var response = (HttpWebResponse)request.GetResponse(); switch (response.StatusCode) { case HttpStatusCode.Redirect: case HttpStatusCode.TemporaryRedirect: case HttpStatusCode.MovedPermanently: this.Url = new Uri(uri, response.Headers["Location"]).ToString(); break; } } }
Now comes the part that illustrates the advantage of using a proxy class manually created with wsdl.exe instead of a service reference. In a manually created proxy class. change class declaration from
public partial class WebApiProxy : System.Web.Services.Protocols.SoapHttpClientProtocol
to
public partial class WebApiProxy : SoapHttpClientProtocolWithRedirect
Now call the DoLogin method as follows.
var apiClient = new WebApiProxy(GetServiceUrl());
You will notice that 302 redirection is handled by code in your SoapHttpClientProtocolWithRedirect class.
Another advantage is that you do not have to fear that any other developer will update the service link and lose the changes you made to the proxy class, since you manually created it. Hope this helps.
Original answer
Why don't you just include the entire URL for the production / local service in the configuration file? This way you can initiate a call with the appropriate URL in the appropriate place.
In addition, I would refrain from using the service link in any code intended for production. One way to use the asmx service without a service reference would be to generate a WebApiProxy.cs file using the wsdl.exe tool. Now you can simply include the WebApiProxy.cs file in your project and create an instance, as shown below.
var apiClient = new WebApiProxy(GetServiceUrl());
Here is the GetServiceUrl () method. Use the configuration repository to further delimit and improve validation.
private string GetServiceUrl() { try { return _configurationRepository.AppSettings[ _configurationRepository.AppSettings["WebApiInstanceToUse"]]; } catch (NullReferenceException ex) {
Your configuration file may then contain the following information in this section.
<add key="StagingWebApiInstance" value="http://mystagingserver/myProduct/webservices/webapi.asmx "/> <add key="ProductionWebApiInstance" value="https://www.myurl.com/myProduct/webservices/webapi.asmx"/> <add key="WebApiInstanceToUse" value="ProductionWebApiInstance"/>
I would also refrain from concatenating strings using + overload. When you do this once, it does not occur as a too strong impact on performance, but if there is a lot of concatenation in the code, it will lead to a big difference in runtime compared to using StringBuilder. Check out http://msdn.microsoft.com/en-us/library/ms228504.aspx for more information on why using StringBuilder improves performance.