为什么会失败WCF遇到302响应时,将调用一个SOAP服务? [英] Why would WCF fail to call a SOAP service when a 302 response is encountered?

查看:398
本文介绍了为什么会失败WCF遇到302响应时,将调用一个SOAP服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了与拨打电话WCF登录启动应用程序。我生成一个服务引用的客户端代码。它工作正常,谁拥有本地安装到他们的网络的客户提供服务。然而,有一个SaaS环境中,还有,那里同样是这些服务是由企业的权力,是控制。在SaaS环境中,我被告知,登录是失败。使用招调查,我发现服务调用即可登录返回HTML,具体而言,该网页列出了从的.asmx所有可用的方法。

I have written an application that starts with making a WCF call to login. I generated the client code with a service reference. It works fine for clients who have their services installed locally to their network. However, there is a saas environment, as well, where these same services are controlled by the corporate powers that be. In the saas environment, I was informed that the login was failing. Investigating using Fiddler, I found that the service call to login is returning HTML, specifically, the web page listing all the available methods from the .asmx.

在SaaS环境中有一个小小的怪癖可以在这里引起的问题,但我不知道如何验证这是否是问题,也不知道怎样去解决它,如果它是问题。的怪癖是服务器重定向(302)电话

The saas environment has one little quirk which may be causing the problem here, but I don't know how to verify that this is the problem, nor how to solve it if it is the problem. The quirk is that the server redirects (302) the call.

客户端代码:


    client.Endpoint.Address = new EndpointAddress("http://" + settings.MyURL + "/myProduct/webservices/webapi.asmx");
    client.DoLogin(username, password);

发送到服务器的原始数据,重定向之前,包括在S:信封的XML标记。发送到服务器重定向信封时,XML标记:注意缺少的小号

The raw data sent to the server, before the redirect, includes the s:Envelope XML tag. Notice the missing s:Envelope XML tag when sending to the redirected server:


    GET https://www.myurl.com/myProduct/webservices/webapi.asmx HTTP/1.1
    Content-Type: text/xml; charset=utf-8
    VsDebuggerCausalityData: uIDPo7TgjY1gCLFLu6UXF8SWAoEAAAAAQxHTAupeAkWz2p2l3jFASiUPHh+L/1xNpTd0YqI2o+wACQAA
    SOAPAction: "http://Interfaces.myProduct.myCompany.com/DoLogin"
Accept-Encoding: gzip, deflate
    Host: www.gotimeforce2.com
    Connection: Keep-Alive

我如何获得工作的这个愚蠢的事情。

How do I get this silly thing working?

修改? 值得注意的是,我使用,而不是旧的ASMX / Wsdl.exe用/ WEB-参考WCF / svcutil.exe的/服务的参考。否则,这个话题未来的读者,由拉吉建议WSDL的解决方案将是一个很好的解决方案。如果您看到了这个问题,并正在使用WSDL技术,看到Raj的出色答卷。

It is worth noting that I am using WCF/svcutil.exe/service-reference rather than the older ASMX/wsdl.exe/web-reference. Otherwise, for future readers of this topic, the wsdl solution suggested by Raj would have been a great solution. If you are seeing this issue and are using the wsdl technique, see Raj's excellent answer.

EDIT2:做了一大堆的研究WCF后和302,这听起来像他们只是没有发挥好在一起,也不存在似乎是给WCF API自定义代码来处理这种情况的简单方法。因为我有服务器无法控制,我已经吸了起来,重新生成我的API作为一个Web引用,并正在使用Raj的解决方案。

After doing a bunch of research into WCF and 302, it sounds like they just don't play well together, nor does there appear to be a simple way of giving the WCF api custom code to handle the situation. As I have no control over the server, I have sucked it up and re-generated my api as a web-reference and am using Raj's solution.

EDIT3 更新了标题,以更好地反映该解决方案,目前该问题的原因是可理解的。原标题:为什么WCF不包括s-:信封上的重定向

Updated the title to better reflect the solution, now that the cause of the issue is understood. Original title: Why would WCF not include s:Envelope on a redirect?

推荐答案

好吧,所以我做了一些挖就这个问题和?试图复制在我身边的问题。我能复制这个问题,并找到解决它。但是我不知道有多好,这将在您的案件,不适用,因为它是依赖于与管理负载均衡服务器组接口。以下是调查结果。

Ok, So I did some digging on this and tried to replicate the issue on my side. I was able to replicate the issue and find a solution to it as well. However I'm not sure how well this will apply in your case since it is dependent on interfacing with the server team that manages the load balancer. Here are the findings.

看着 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 你注意到在HTTP状态代码302和303的解释下面的附录。

Looking at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html you notice the following addendum in the explanation for HTTP Status Codes 302 and 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下面的解释

Further looking at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes you notice the following explanation for the HTTP status codes 302, 303 and 307.

302实测值:
这是行业典范实践矛盾的标准。在HTTP / 1.0规范(RFC 1945)所需的客户端执行临时重定向(原描述句话被动了暂时),,但流行的浏览器中实现302与303查看其它的功能。因此,HTTP / 1.1中加入的状态代码303和307的两种行为之间进行区分。然而,某些Web应用程序和框架使用302状态代码,就好像它是在303

302 Found : This is an example of industry practice contradicting the standard. The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 to distinguish between the two behaviors. However, some Web applications and frameworks use the 302 status code as if it were the 303.

303查看其它(因为HTTP / 1.1):
到请求的响应可以在另一个URI使用GET方法找到。 当响应POST接收(或PUT / DELETE),应假定服务器接收到的数据和重定向页面应该有独立的GET消息发出。
这里是在一个正常的客户机/服务器交互的基本流程

303 See Other (since HTTP/1.1): The response to the request can be found under another URI using a GET method. When received in response to a POST (or PUT/DELETE), it should be assumed that the server has received the data and the redirect should be issued with a separate GET message. Here is the basic flow in a normal Client/Server Interaction

307临时重定向(因为HTTP / 1.1):
在这种情况下,该请求应该用另一URI中重复;然而,未来的请求还是应该使用原来的URI。的 相反至302是如何历史执行,不允许请求方法重新发出原始的请求时被改变。例如,POST请求应该使用其他POST请求被重复。

307 Temporary Redirect (since HTTP/1.1): In this case, the request should be repeated with another URI; however, future requests should still use the original URI. In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. For instance, a POST request should be repeated using another POST request.

所以根据这一点,我们能够解释的行为WCF调用它发送一个GET请求不带S:信封上的302重定向。这无疑将无法在客户端。

So according to this, we are able to explain the behavior of the WCF call which sends a GET request without the s:Envelope on the 302 redirect. This will undoubtedly fail on the client side.

解决这个的最简单方法是让服务器在响应中返回,而不是302 Found状态码307临时重定向。这就是你需要一个管理上的负载均衡器重定向规则的服务器团队的帮助。我在本地测试了这一点,并在客户端代码消费与服务引用服务无缝地执行即使有307临时重定向呼叫。

The easiest way of fixing this is to have the server return a 307 Temporary Redirect instead of a 302 Found status code in the response. Which is where you need the help of the Server Team that manages the redirect rules on the load balancer. I tested this out locally and the client code consuming the service with the Service Reference seamlessly executes the call even with the 307 Temporary Redirect.

在事实上,你可以测试出这一切与解决方案我已经上传至Github上的 这里 。我已经更新这说明了一个服务引用,而不是一个WSDL生成的代理类消耗ASMX服务的利用率。

In fact you could test this all out with the solution I've uploaded to Github Here. I've updated this to illustrate the utilization of a service reference instead of a wsdl generated proxy class to consume the asmx service.

但是,如果从302变化发现307临时重定向是不是在你的环境是可行的,那么我会建议使用任何的解决方法1 (这不应该有问题无论是在响应一个302或307状态码)或使用我的原来的答案这将通过基于配置文件的设置正确的URL直接访问该服务解决此问题。希望这有助于!

However if the change from 302 Found to 307 Temporary Redirect is not feasible in your environment, then I would suggest using either Solution 1 (which shouldn't have a problem whether it is a 302 or 307 status code in the response) or using my original answer which would resolve this by directly accessing the service at the correct URL based on the setting in the config file. Hope this helps!

解决方案1 ​​

如果您没有访问对生产的配置文件,或者如果你只是简单的不想使用多个URL中的配置文件,您可以使用此下面的办法。链接到Github上回购含有样品溶液的 点击这里

If you do not have access to the config files on production or if you just plain don't want to use the multiple URLs in the config file, you could use this following approach. Link to Github repo containing sample solution Click Here

基本上,如果你发现通过Wsdl.exe用,你会发现,服务代理类从 System.Web.Services.Protocols导出生成的文件自动.SoapHttpClientProtocol 。该类有一个受保护的方法 System.Net.WebRequest GetWebRequest(URI URI),你可以重写。在这里,你可以添加一个方法来检查,看是否有302临时重定向是 HttpWebRequest.GetResponse()方法的结果。如果是这样,你可以将URL设置为新网址在位置如下响应头返回。

Basically, if you notice the file auto generated by wsdl.exe you will notice that the service proxy class derives from System.Web.Services.Protocols.SoapHttpClientProtocol. This class has a protected method System.Net.WebRequest GetWebRequest(Uri uri) that you can override. In here you could add a method to check to see if a 302 temporary redirect is the result of HttpWebRequest.GetResponse() method. If so, you can set the Url to the new Url returned in the Location header of the response as follows.

this.Url =新的URI(URI,response.Headers [所在地])的ToString();

因此,创建一个类。所谓SoapHttpClientProtocolWithRedirect如下:

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;
        }
    }
}

现在来,说明了部分使用使用Wsdl.exe用,而不是一个服务引用手动生成的代理类的优势。在手动创建的代理类。从修改

Now comes the part that illustrates the advantage of using a proxy class manually generated using wsdl.exe instead of a service reference. In the manually created proxy class. modify the class declaration from

public partial class WebApiProxy : System.Web.Services.Protocols.SoapHttpClientProtocol

public partial class WebApiProxy : SoapHttpClientProtocolWithRedirect

现在调用DoLogin方法如下。

Now invoke the DoLogin method as follows.

var apiClient = new WebApiProxy(GetServiceUrl());
//TODO: Add any required headers etc.
apiClient.DoLogin(username,password);

您会发现,302重定向是由您SoapHttpClientProtocolWithRedirect类的代码顺利处理。

You will notice that the 302 redirect is handled smoothly by the code in your SoapHttpClientProtocolWithRedirect class.

另外一个优点是,通过这样做,你就不必担心其他一些发展商将刷新服务引用,失去你的代理类所做的更改既然你已经手动生成它。希望这有助于。

One other advantage is that, by doing this, you will not have to fear that some other developer is going to refresh the service reference and lose the changes that you made to the proxy class since you had manually generated it. Hope this helps.

原来的答案

你为什么不只是包括在配置文件制作/本地服务整个网址是什么?这样,你可以启动与在适当的位置适当的URL呼叫。

Why don't you just include the entire url for production/local service in the config file? That way you can initiate the call with the appropriate url in the appropriate location.

另外,我想从使用指定用于生产任何代码的服务引用避免。使用ASMX服务未经服务引用的一个方法是生成使用Wsdl.exe工具的WebApiProxy.cs文件。现在,你可以只包含WebApiProxy.cs文件在您的项目和实例如下图所示。

Also, I would refrain from using a service reference in any code destined for production. One way of using the asmx service without a service reference would be to generate the WebApiProxy.cs file using the wsdl.exe tool. Now you can just include the WebApiProxy.cs file in your project and instantiate as shown below.

var apiClient = new WebApiProxy(GetServiceUrl());
//TODO: Add any required headers etc.
apiClient.DoLogin(username,password);

下面是GetServiceUrl()方法。使用配置库,以进一步分离,提高可测性。

Here is the GetServiceUrl() method. Use a Configuration Repository to further decouple and improve testability.

private string GetServiceUrl()
    {
        try
        {
            return
            _configurationRepository.AppSettings[
                _configurationRepository.AppSettings["WebApiInstanceToUse"]];
        }
        catch (NullReferenceException ex)
        {
            //TODO: Log error
            return string.Empty;
        }
    }



那么你的配置文件可以包含在以下信息。栏目

Then your config file can contain the following information in the section.

<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的提高性能的详细信息。

Also I would refrain from concatenating strings using the + overload. When doing it once, it doesn't come across as too much of a performance impact but if you have many concatenations like this throughout the code, it would lead to a big difference in execution times compared to using a StringBuilder. Check http://msdn.microsoft.com/en-us/library/ms228504.aspx for more information on why using a StringBuilder improves performance.

这篇关于为什么会失败WCF遇到302响应时,将调用一个SOAP服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆