WCF数据服务错误 - 302在与HTTPS +负载均衡器相关的SaveChanges()上移动了?

时间:2011-08-23 13:36:39

标签: .net wcf rest odata wcf-data-services

问题: DataServiceContext.SaveChanges()失败并显示“302 - 移动”响应。

背景/可疑原因:负载均衡器! - 我们最近更改了基础架构,以便我们的Web服务器现在位于负载均衡器后面,负载均衡器也可以处理ssl。客户端将服务称为HTTPS,但IIS最终处理HTTP请求,因为SSL由负载均衡器完成(我相信大多数人都熟悉这种类型的设置)。无论如何,我们最终得到的是包含使用HTTP而不是HTTPS的URI的订阅源。 (请参阅下面的GET请求/响应,在响应中使用http而不是httpS)。这种行为很奇怪,因为当我调用saveChanges()时它会发送一个MERGE(如预期的那样),然后我回到302:

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

但是WCF一直在使用HTTP(大约50次)反复尝试MERGE,然后最终抛出异常消息,“”处理此请求时出错。“,内部异常消息是”Found“。:)

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

必须有一些我找不到的配置设置/属性。 Viteks answer对类似的问题让我感到有些害怕。

这是来自fiddler的原始GET请求

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

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

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
.
.
.

2 个答案:

答案 0 :(得分:2)

此处的问题可能是自我/链接编辑链接是HTTP,而不是HTTPS,因此客户端无法使用链接访问数据。 Per Vitek的scary answer

不幸的是服务器不知道它在哪里。

我可以想到几个选项(它们都不是特别好):

  1. 使用自定义IDataServiceHost2通过AbsoluteServiceUri属性告知数据服务其根目录是https:// ....这样数据服务应该生成适当的URL
  2. 实现一个WCF行为,剥离http:// urls并用https://替换它们,这将涉及大量缓冲等。检查此JSONP example修改服务器的响应。
  3. -Alex

答案 1 :(得分:0)

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

问题#1:使用https在负载均衡器后面的IIS中托管的WCF数据服务使用http URI而不是https创建源。这是可以理解的,也是我对这个问题的第一次猜测。通过大量挖掘,我遇到了this文章,提供了一个非常直接的解决方案。

在我的服务的构造函数中,我连接到处理请求事件

ProcessingPipeline.ProcessingRequest += ProcessingPipeline_ProcessingRequest;

在事件处理程序中,我使用上面提到的文章并做了一些调整,基本上从clientExpectsUri获取主机,方案和端口,并将它们应用于请求Uri。我也只是寻找一个我称之为“X-Client-Expects-RootUri”的自定义标题,防火墙外的客户端如果需要有效的源,则需要发送该标头。

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

我真的希望对此设计有一些反馈,因为我基本上要求客户端告诉我如何通过自定义标头返回URI。我将在未来修改负载均衡器以自动添加这些标头。

问题#2:没有为MERGE和其他非RFC http方法配置负载均衡器。这个问题的解决方案很简单,只需在DataServiceContext上将usePostTunneling设置为true即可。如果有人有脚本为cisco 11503启用非RFC http方法,我很乐意拥有它们。 ; - )