好的,所以我对此进行了一些挖掘,并尝试在我这边复制这个问题。我能够复制这个问题并找到解决方案。但是,我不确定这在您的情况下有多适用,因为它取决于与管理负载均衡器的服务器团队的接口。以下是调查结果。
看着http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
您会注意到 HTTP 状态代码 302 和 303 的解释中的以下附录。
302 找到
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章看其他
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.
进一步查看http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
您会注意到以下对 HTTP 状态代码 302、303 和 307 的解释。
302 找到:这是行业实践与标准相矛盾的一个例子。 HTTP/1.0 规范 (RFC 1945) 要求客户端执行临时重定向(最初的描述短语是“Moved Temporarily”),但流行的浏览器实现了 302 和 303 See Other 的功能。因此,HTTP/1.1添加了状态码303和307来区分这两种行为。但是,某些 Web 应用程序和框架使用 302 状态代码,就好像它是 303 一样。
303 查看其他(自 HTTP/1.1 起):可以使用 GET 方法在另一个 URI 下找到对请求的响应。当收到对 POST(或 PUT/DELETE)的响应时,应假定服务器已收到数据,并且应使用单独的 GET 消息发出重定向。这是正常客户端/服务器交互的基本流程
307 临时重定向(自 HTTP/1.1 起):在这种情况下,应该使用另一个 URI 重复请求;但是,将来的请求仍应使用原始 URI。与历史上 302 的实现方式相反,重新发出原始请求时不允许更改请求方法。例如,应使用另一个 POST 请求重复一个 POST 请求。
因此,据此,我们能够解释 WCF 调用的行为,该调用在 302 重定向上发送不带 s:Envelope 的 GET 请求。这在客户端无疑会失败。
解决此问题的最简单方法是让服务器在响应中返回 307 临时重定向,而不是 302 Found 状态代码。这就是您需要管理负载均衡器上的重定向规则的服务器团队的帮助。我在本地对此进行了测试,即使使用 307 临时重定向,使用服务引用的服务的客户端代码也可以无缝执行调用。
事实上,你可以使用我上传到 Github 的解决方案来测试这一切Here https://github.com/rexthuhking/WebServiceRedirectHandler。我对此进行了更新,以说明如何利用服务引用而不是 wsdl 生成的代理类来使用 asmx 服务。
但是,如果从 302 Found 更改为 307 临时重定向在您的环境中不可行,那么我建议使用解决方案1 (无论响应中的状态码是 302 还是 307,都不应该有问题)或使用我的原答案这将通过根据配置文件中的设置直接访问正确 URL 的服务来解决此问题。希望这可以帮助!
解决方案1
如果您无权访问生产环境中的配置文件,或者您只是不想在配置文件中使用多个 URL,则可以使用以下方法。包含示例解决方案的 Github 存储库链接点击这里 https://github.com/rexthuhking/WebServiceRedirectHandler
基本上,如果您注意到 wsdl.exe 自动生成的文件,您会注意到服务代理类派生自System.Web.Services.Protocols.SoapHttpClientProtocol
。这个类有一个受保护的方法System.Net.WebRequest GetWebRequest(Uri uri)
您可以覆盖。在这里你可以添加一个方法来检查 302 临时重定向是否是以下结果的结果HttpWebRequest.GetResponse()
方法。如果是这样,您可以将 Url 设置为返回的新 UrlLocation响应的标头如下。
this.Url = new Uri(uri, response.Headers["Location"]).ToString();
因此,创建一个名为 SoapHttpClientProtocolWithRedirect 的类,如下所示。
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;
}
}
}
现在的部分说明了使用 wsdl.exe 手动生成的代理类而不是服务引用的优点。在手动创建的代理类中。修改类声明
public partial class WebApiProxy : System.Web.Services.Protocols.SoapHttpClientProtocol
to
public partial class WebApiProxy : SoapHttpClientProtocolWithRedirect
现在按如下方式调用 DoLogin 方法。
var apiClient = new WebApiProxy(GetServiceUrl());
//TODO: Add any required headers etc.
apiClient.DoLogin(username,password);
您会注意到 SoapHttpClientProtocolWithRedirect 类中的代码可以顺利处理 302 重定向。
另一个优点是,通过这样做,您不必担心其他开发人员会刷新服务引用并丢失您对代理类所做的更改,因为您手动生成了代理类。希望这可以帮助。
原答案
为什么不在配置文件中包含生产/本地服务的完整 URL?这样您就可以在适当的位置使用适当的 url 发起呼叫。
另外,我不会在任何用于生产的代码中使用服务引用。在没有服务引用的情况下使用 asmx 服务的一种方法是使用 wsdl.exe 工具生成 WebApiProxy.cs 文件。现在,您只需将 WebApiProxy.cs 文件包含在项目中并实例化,如下所示。
var apiClient = new WebApiProxy(GetServiceUrl());
//TODO: Add any required headers etc.
apiClient.DoLogin(username,password);
这是 GetServiceUrl() 方法。使用配置存储库进一步解耦并提高可测试性。
private string GetServiceUrl()
{
try
{
return
_configurationRepository.AppSettings[
_configurationRepository.AppSettings["WebApiInstanceToUse"]];
}
catch (NullReferenceException ex)
{
//TODO: Log error
return string.Empty;
}
}
然后您的配置文件可以在该部分包含以下信息。
<add key="StagingWebApiInstance" value="http://mystagingserver/myProduct/webservices/webapi.asmx "/>
<add key="ProductionWebApiInstance" value="https://www.myurl.com/myProduct/webservices/webapi.asmx"/>
<!-- Identify which WebApi.asmx instance to Use-->
<add key="WebApiInstanceToUse" value="ProductionWebApiInstance"/>
另外,我会避免使用 + 重载来连接字符串。当执行一次时,它不会对性能产生太大影响,但如果在整个代码中有很多这样的串联,与使用 StringBuilder 相比,它将导致执行时间的巨大差异。查看http://msdn.microsoft.com/en-us/library/ms228504.aspx
有关为什么使用 StringBuilder 可以提高性能的更多信息。