WCF数据服务错误 - 302继续前行的SaveChanges()与HTTPS +负载均衡?负载均衡、错误、继续、数据

2023-09-04 03:07:21 作者:青衣剑客

问题: DataServiceContext.SaveChanges()失败,302 - 感动的回应。

背景/可疑原因:负载均衡! - 最近,我们改变了我们的基础设施,使我们的网络服务器现在坐在后面的负载均衡器也处理SSL。客户解决服务为HTTPS,但IIS最终处理HTTP请求,因为SSL是由负载均衡器做(我相信大多数人都熟悉这种类型的设置)。无论如何,我们最终得到的是一个进料包含使用HTTP而不是HTTPS的URI。 (见GET请求/响应与以下中的HTTP响应,而不是HTTPS)。该行为是pretty的奇怪,因为当我打电话调用SaveChanges()发送一个MERGE(如预期),我拿回302:

  HTTP / 1.1 302对象移动
位置: https://some.domain.org/CMSProfileService/ProfileDataService.svc/Mails(guid'80fef993-a4b5-4343-a908-28c2c6517a81')
连接:关闭
 

但WCF一直在试图融合和通过使用HTTP(约50倍),那么最后抛出一个异常,并显示消息,在处理此请求时出错。,是发现内excepion消息。:)

当我直接指向服务器(绕过负载均衡和SSL)一切工作正常。一切都还正常工作时,SSL在IIS中直接注册。

必须有一些配置设置/物业,我没有找到。 Viteks answer一个类似的问题让我害怕了一下。

这是提琴手原始GET请求

  GET https://some.domain.org/CMSProfileService2/ProfileDataService.svc/Mails()?$filter=Status%20eq%20'Queued'&$orderby=Timestamp&$expand=Attachments HTTP / 1.1
用户代理:微软ADO.NET数据服务
DataServiceVersion:1.0; NETFX
MaxDataServiceVersion:2.0; NETFX
用户名:
接受:应用程序/原子+ XML,application / xml进行
接收字符集:UTF-8
主持人:some.domain.org
连接:保持活动
 
WCF

,这里是原始响应(修剪):

  HTTP / 1.1 200 OK
设置Cookie:ARPT = RPZVOOS192.168.94.118CKOUM; PATH = /
缓存控制:无缓存
内容长度:45849
内容类型:应用程序/原子+ XML;字符集= UTF-8
服务器:Microsoft-IIS / 7.5
DataServiceVersion:1.0;
的X ASPNET-版本:4.0.30319
的X已启动方式:ASP.NET
日期:星期一,2011 14时56分46秒格林尼治标准​​时间8月22日

< XML版本=1.0编码=UTF-8独立=是&GT?;
<饲料的xml:基地=htt​​p://some.domain.org/CMSPRofileService2/ProfileDataService.svc/的xmlns:D =htt​​p://schemas.microsoft.com/ado/2007/08/dataservices的xmlns: M =htt​​p://schemas.microsoft.com/ado/2007/08/dataservices/metadata的xmlns =htt​​p://www.w3.org/2005/Atom>
  <标题类型=文本>邮件< /标题>
  &LT; ID&GT; HTTP://some.domain.org/CMSProfileService2/ProfileDataService.svc/Mails< / ID&GT;
  &LT;更新&GT; 2011-08-22T14:56:47Z&LT; /更新&GT;
  &LT;链接相对=自我称号=邮件的href =邮件/&GT;
  &LT;进入&GT;
    <id>http://some.domain.org/CMSPRofileService2/ProfileDataService.svc/Mails(guid'c7edb158-7a61-4fca-a40e-7f4a3a0b2bbd')</id>
    &LT;标题类型=文本&GT;&LT; /标题&GT;
    &LT;更新&GT; 2011-08-22T14:56:47Z&LT; /更新&GT;
    &LT;笔者&GT;
      &LT;名称/&GT;
    &LT; /笔者&GT;
    &LT;链接相对=编辑称号=邮件的href =邮件(guid'c7edb158-7a61-4fca-A40E-7f4a3a0b2bbd')/&GT;
    &LT;链接相对=htt​​p://schemas.microsoft.com/ado/2007/08/dataservices/related/Attachments类型=应用程序/原子+ XML;类型=频道标题=附件的href =邮件(guid'c7edb158-7a61-4fca-A40E-7f4a3a0b2bbd')/附件&GT;
      &LT; M:内嵌&GT;
        &LT;饲料&GT;
          &LT;标题类型=文本&GT;附件&LT; /标题&GT;
          <id>http://some.domain.org/CMSPRofileService2/ProfileDataService.svc/Mails(guid'c7edb158-7a61-4fca-a40e-7f4a3a0b2bbd')/Attachments</id>
          &LT;更新&GT; 2011-08-22T14:56:47Z&LT; /更新&GT;
          &LT;笔者&GT;
            &LT;名称/&GT;
          &LT; /笔者&GT;
          &LT;链接相对=自我称号=附件的href =邮件(guid'c7edb158-7a61-4fca-A40E-7f4a3a0b2bbd')/附件/&GT;
        &LT; /饲料&GT;
      &LT; / M:内嵌&GT;
    &LT; /链接&GT;
    &LT;类别术语=XXX.YYY.Profile.Repository.Mail计划=htt​​p://schemas.microsoft.com/ado/2007/08/dataservices/scheme/&GT;
    &LT;内容类型=应用程序/ XML&GT;
      &LT; M:性状&gt;
        &LT; D​​:MailId M:TYPE =Edm.Guid&GT; c7edb158-7a61-4fca-A40E-7f4a3a0b2bbd&LT; / D:MailId&GT;
        &LT; D​​:时间戳M:TYPE =Edm.DateTime&GT; 2011-07-28T12:51:37.69&LT; / D:时间戳&GT;
        &LT; D​​:应用code取代; EREF&LT; / D:应用code取代;
        &LT; D​​:状态&gt;&排队LT; / D:状态
。
。
。
 

解决方案

终于解决了这个讨厌的问题。实际上有两个问题。

问题#1:WCF数据服务在IIS中承载的背后负载平衡器以https创建了HTTP URI,而不是HTTPS饲料。这是可以理解的,是我第一次的猜测到这个问题。通过大量挖我碰到其中提供了pretty的直截了当的解决方案,这的文章。

在我服务的构造我挂接到处理请求事件

  ProcessingPipeline.ProcessingRequest + = ProcessingPipeline_ProcessingRequest;
 

在事件处理程序中,我用提到的文章,并取得了一定的调整,基本上都坐主机,方案和端口从clientExpectsUri,并将其应用于请求URI。我也只是看的,我叫X-客户来料,RootUri这外面的防火墙需要客户端发送,如果他们想要有效供稿自定义标题。

 静态无效ProcessingPipeline_ProcessingRequest(对象发件人,DataServiceProcessingPipelineEventArgs E)
{
    如果(e.OperationContext.RequestHeaders.AllKeys.Contains(ClientExpectsUriKey))
        ProcessUri(新的URI(e.OperationContext.RequestHeaders [ClientExpectsUriKey]));
}

私有静态无效ProcessUri(URI clientExpectsRootUri)
{
    如果(clientExpectsRootUri!= NULL)
    {
        VAR requestUri = OperationContext.Current.IncomingMessageProperties.ContainsKey(MicrosoftDataServicesRequestUri)
                                ? OperationContext.Current.IncomingMessageProperties [MicrosoftDataServicesRequestUri作为开放的我们:HttpContext.Current.Request.Url;

        VAR serviceUri分别= clientExpectsRootUri;

        VAR serviceUriBuilder =新UriBuilder(serviceUri分别);

        VAR requestUriBuilder =新UriBuilder(requestUri)
                                    {
                                        主机= serviceUriBuilder.Host,
                                        计划= serviceUriBuilder.Scheme,
                                        端口= serviceUriBuilder.Port
                                    };

        如果(!serviceUriBuilder.Path.EndsWith(/))//基URI应以斜线结束...
            serviceUriBuilder.Path = serviceUriBuilder.Path + =/;

        OperationContext.Current.IncomingMessageProperties [MicrosoftDataServicesRootUri] = serviceUriBuilder.Uri;
        OperationContext.Current.IncomingMessageProperties [MicrosoftDataServicesRequestUri] = requestUriBuilder.Uri;
    }
}
 

我真的喜欢上了这设计的一些反馈意见,因为我基本上要求客户告诉我如何通过自定义标题返回的URI。我会寻找到修改负载平衡器在未来自动添加这些标头。

问题2:负载均衡器是未配置为MERGE和其他非RFC HTTP方法。要解决这个问题很简单,只要设置 usePostTunneling 以上的DataServiceContext如此。如果任何人有脚本,使非RFC的HTTP方法思科11503,我很想有他们。 ; - )

Problem: DataServiceContext.SaveChanges() fails with "302 - moved" response.

Background/Suspected Cause: Load balancer! - We recently changed our infrastructure so that our web servers now sit behind a load balancer which also handles the ssl. Clients address the service as HTTPS but IIS ends up processing HTTP requests since the SSL was done by the load balancer(I am sure most of you are familiar with this type of setup). Anyway, what we end up with is a feed that contains URIs that are using HTTP rather than HTTPS. (see GET request/response below with http in the response instead of httpS). The behavior is pretty strange, as when I call saveChanges() it sends a MERGE(as expected), and I get back a 302:

HTTP/1.1 302 Object moved
Location: https://some.domain.org/CMSProfileService/ProfileDataService.svc/Mails(guid'80fef993-a4b5-4343-a908-28c2c6517a81')
Connection: close

but WCF keeps trying the MERGE over and over using HTTP(about 50 times) then finally throws an exception with the message, ""An error occurred while processing this request.", inner excepion message is "Found". :)

When I point directly at the server(bypassing the loadbalancer and ssl) everything works fine. Everything also works fine when SSL is registered directly in IIS.

There must be some config setting/property that I am not finding. Viteks answer to a similar question scares me a bit.

This is the raw GET request from fiddler

GET https://some.domain.org/CMSProfileService2/ProfileDataService.svc/Mails()?$filter=Status%20eq%20'Queued'&$orderby=Timestamp&$expand=Attachments HTTP/1.1
User-Agent: Microsoft ADO.NET Data Services
DataServiceVersion: 1.0;NetFx
MaxDataServiceVersion: 2.0;NetFx
UserName: 
Accept: application/atom+xml,application/xml
Accept-Charset: UTF-8
Host: some.domain.org
Connection: Keep-Alive

and here is the raw response(trimmed):

HTTP/1.1 200 OK
Set-Cookie: ARPT=RPZVOOS192.168.94.118CKOUM; path=/
Cache-Control: no-cache
Content-Length: 45849
Content-Type: application/atom+xml;charset=utf-8
Server: Microsoft-IIS/7.5
DataServiceVersion: 1.0;
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 22 Aug 2011 14:56:46 GMT

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="http://some.domain.org/CMSPRofileService2/ProfileDataService.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Mails</title>
  <id>http://some.domain.org/CMSProfileService2/ProfileDataService.svc/Mails</id>
  <updated>2011-08-22T14:56:47Z</updated>
  <link rel="self" title="Mails" href="Mails" />
  <entry>
    <id>http://some.domain.org/CMSPRofileService2/ProfileDataService.svc/Mails(guid'c7edb158-7a61-4fca-a40e-7f4a3a0b2bbd')</id>
    <title type="text"></title>
    <updated>2011-08-22T14:56:47Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" title="Mail" href="Mails(guid'c7edb158-7a61-4fca-a40e-7f4a3a0b2bbd')" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Attachments" type="application/atom+xml;type=feed" title="Attachments" href="Mails(guid'c7edb158-7a61-4fca-a40e-7f4a3a0b2bbd')/Attachments">
      <m:inline>
        <feed>
          <title type="text">Attachments</title>
          <id>http://some.domain.org/CMSPRofileService2/ProfileDataService.svc/Mails(guid'c7edb158-7a61-4fca-a40e-7f4a3a0b2bbd')/Attachments</id>
          <updated>2011-08-22T14:56:47Z</updated>
          <author>
            <name />
          </author>
          <link rel="self" title="Attachments" href="Mails(guid'c7edb158-7a61-4fca-a40e-7f4a3a0b2bbd')/Attachments" />
        </feed>
      </m:inline>
    </link>
    <category term="XXX.YYY.Profile.Repository.Mail" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:MailId m:type="Edm.Guid">c7edb158-7a61-4fca-a40e-7f4a3a0b2bbd</d:MailId>
        <d:Timestamp m:type="Edm.DateTime">2011-07-28T12:51:37.69</d:Timestamp>
        <d:ApplicationCode>EREF</d:ApplicationCode>
        <d:Status>Queued</d:Status
.
.
.

解决方案

Finally solved this nasty issue. There were actually two issues.

Issue #1: WCF Data Service hosted in IIS behind a load balancer with https creates feeds with http URIs rather than https. This is understandable and was my first guess into the issue. Through lots of digging I came across this article which provided a pretty straight forward solution.

In the constructor of my service I hook up to the processing request event

ProcessingPipeline.ProcessingRequest += ProcessingPipeline_ProcessingRequest;

In the event handler, I use the article mentioned and made some tweaks that basically take the host, scheme and port from the clientExpectsUri and applies them to the request Uri. I also just look for a custom header that I called "X-Client-Expects-RootUri" that clients outside the firewall need to send if they want valid feeds.

static void ProcessingPipeline_ProcessingRequest(object sender, DataServiceProcessingPipelineEventArgs e)
{
    if (e.OperationContext.RequestHeaders.AllKeys.Contains(ClientExpectsUriKey))
        ProcessUri(new Uri(e.OperationContext.RequestHeaders[ClientExpectsUriKey]));    
}

private static void ProcessUri(Uri clientExpectsRootUri)
{
    if (clientExpectsRootUri != null)
    {
        var requestUri = OperationContext.Current.IncomingMessageProperties.ContainsKey("MicrosoftDataServicesRequestUri") 
                                ? OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRequestUri"] as Uri : HttpContext.Current.Request.Url;

        var serviceUri = clientExpectsRootUri;

        var serviceUriBuilder = new UriBuilder(serviceUri);

        var requestUriBuilder = new UriBuilder(requestUri)
                                    {
                                        Host = serviceUriBuilder.Host,
                                        Scheme = serviceUriBuilder.Scheme,
                                        Port = serviceUriBuilder.Port
                                    };

        if (!serviceUriBuilder.Path.EndsWith("/")) //the base uri should end with a slash... 
            serviceUriBuilder.Path = serviceUriBuilder.Path += "/";

        OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRootUri"] = serviceUriBuilder.Uri;
        OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRequestUri"] = requestUriBuilder.Uri;
    }
}

I would actually like some feedback on this design, as I am basically asking the client to tell me how to return the URI via a custom header. I will be looking into modifying the load balancer in the future to add these headers automatically.

Issue #2: Load balancer was not configured for MERGE and other non-RFC http methods. The solution to this issue was simple, just set usePostTunneling to true on the DataServiceContext. If anyone has the scripts to enable non-RFC http methods for a cisco 11503, I'd love to have them. ;-)