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

发布于 2024-12-01 00:23:12 字数 4010 浏览 0 评论 0 原文

问题: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 一遍又一遍地尝试 MERGE(大约 50 次),然后最终抛出异常消息“处理此请求时发生错误。”,内部异常消息为“找到”。:)

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

一定有一些我找不到的配置设置/属性。 the-net-linq-providers-translation-to-odata-uri-format/6993670#6993670">类似问题的答案让我有点害怕。

这是来自 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
.
.
.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

悲凉≈ 2024-12-08 00:23:12

这里的问题很可能是 self/links 编辑链接是 HTTP,而不是 HTTPS,因此客户端无法使用这些链接访问数据。 Per Vitek 的 可怕的答案

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

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

  1. 使用自定义 IDataServiceHost2 通过 AbsoluteServiceUri 属性告诉 Data Services 其根目录是 https://...。这样,数据服务应该生成适当的 URL
  2. 实现 WCF 行为,删除 http:// url 并将其替换为 https://,这将涉及大量缓冲等。查看此 JSONP 示例,用于修改服务器的响应。

-亚历克斯

The problem here is likely that the self/links edit links are HTTP, not HTTPS, so the client can't use the links to access the data. Per Vitek's scary answer.

The unfortunate thing is the server doesn't know where it is.

I can think of a couple of options (neither of them particularly nice):

  1. Use a custom IDataServiceHost2 that tells Data Services its root is https://... via the AbsoluteServiceUri property. That way Data Services should produce appropriate URLS
  2. Implement a WCF Behavior that strips out http:// urls and replaces them with https://, this would involve a lot of buffering etc. Checkout this JSONP example which modifies responses from the server.

-Alex

眼眸印温柔 2024-12-08 00:23:12

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

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

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

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 方法配置负载均衡器。这个问题的解决方案很简单,只需设置 usePostTunneling 设置为 true。如果有人有为 cisco 11503 启用非 RFC http 方法的脚本,我很乐意拥有它们。 ;-)

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文