HttpClient从不在IIS上发送请求

本文关键字:请求 IIS HttpClient | 更新日期: 2023-09-27 18:16:33

我遇到了一个奇怪的问题,我尝试了各种方法来让它工作。

我在我的Web API项目中有一个反向代理委托处理程序,它被连接起来拦截对内部资源,文件等的请求,从我们的外部站点到我们DMZ内的内部站点…

using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
namespace Resources.API
{
    public class ProxyHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var routes = new[]{
                "/api/videos",
                "/api/documents"
            };
            // check whether we need to proxy this request
            var passThrough = !routes.Any(route => request.RequestUri.LocalPath.StartsWith(route));
            if (passThrough)
                return await base.SendAsync(request, cancellationToken);
            // got a hit forward the request to the proxy Web API
            return await ForwardRequest(request, cancellationToken);
        }
        private static async Task<HttpResponseMessage> ForwardRequest(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            //Clone the request and forward to the internal proxy site
            var proxyUrl = ConfigurationManager.AppSettings["ProxyUrl"];
            var baseUri = new UriBuilder(proxyUrl);
            //clone the requestUri and point it at the proxy site
            var forwardedUri = new UriBuilder(request.RequestUri)
            {
                Scheme = baseUri.Scheme,
                Host = baseUri.Host,
                Port = baseUri.Port
            };
            var forwardRequest = new HttpRequestMessage(request.Method, forwardedUri.Uri);
            if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put)
            {
                var stream = new MemoryStream();
                await request.Content.CopyToAsync(stream);
                stream.Seek(0, SeekOrigin.Begin);
                forwardRequest.Content = new StreamContent(stream);
                //copy the content headers
                foreach (var header in request.Content.Headers)
                {
                    forwardRequest.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
                }
            };
            forwardRequest.Version = request.Version;
            foreach (var prop in request.Properties)
            {
                forwardRequest.Properties.Add(prop);
            }
            foreach (var header in request.Headers)
            {
                forwardRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }
            var client = new HttpClient(new HttpClientHandler(), disposeHandler: false);
            var task = await Task.Factory
               .StartNew(async () => await client.SendAsync(forwardRequest, HttpCompletionOption.ResponseHeadersRead,
                   cancellationToken),
                   CancellationToken.None,
                   TaskCreationOptions.LongRunning,
                   TaskScheduler.Default);
            try
            {
                task.Wait(cancellationToken);
            }
            catch (Exception e)
            {
                return new HttpResponseMessage(HttpStatusCode.InternalServerError)
                {
                    Content =
                        new ObjectContent<HttpError>(new HttpError(e, includeErrorDetail: true),
                            new JsonMediaTypeFormatter())
                };
            }
            return task.Result;
        }
    }
}

Edit:也尝试等待并返回任务…

            try
            {
                return await task;
            }

这在IIS Express 8.0上运行良好,但在Windows 7 Professional(我的开发机器)上的IIS 7.5或Windows Server 2012上的IIS 8.0中不起作用。

创建的HttpClient从未真正通过网络发送请求(由Fiddler检查),最终超时并抛出带有子TaskCanceledExceptionAggregateException

task.Wait上设置断点,我注意到,由于某种原因,断点被击中10次,而不是通过IIS Express运行时一次。

我已经尝试了各种各样的东西,试图让这个工作,包括很多搜索谷歌和SO,但似乎没有工作。

有人知道为什么会这样吗?或者能解释我做错了什么?

HttpClient从不在IIS上发送请求

明白了。必须更改请求的Host标头才能正确发送。它实际上忽略了RequestUri,并使用Host头来决定实际发送请求的位置。

forwardRequest.Headers.Host = forwardRequest.RequestUri.Host;

现在就像一个魅力,IIS现在将正确地发送请求。仍然让我想知道为什么IIS Express似乎不需要更改Host头!

完整的代码…添加了X-Forwarded-ForX-Forwarded-Host,以更好地衡量。

using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
namespace Resources.API
{
    public class ProxyHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var routes = new[]{
                "/api/videos",
                "/api/documents"
            };
            // check whether we need to proxy this request
            var passThrough = !routes.Any(route => request.RequestUri.LocalPath.StartsWith(route));
            if (passThrough)
                return await base.SendAsync(request, cancellationToken);
            // got a hit forward the request to the proxy Web API
            //return GetResponseFromProxy(request);
            //Nicer method using HttpClient - but it doesn't work on IIS!
            return await ForwardRequest(request, cancellationToken);
        }
        private static async Task<HttpResponseMessage> ForwardRequest(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            //Clone the request and forward to the internal proxy site
            var proxyUrl = ConfigurationManager.AppSettings["ProxyUrl"];
            var baseUri = new UriBuilder(proxyUrl);
            //clone the requestUri and point it at the proxy site
            var forwardedUri = new UriBuilder(request.RequestUri)
            {
                Scheme = baseUri.Scheme,
                Host = baseUri.Host,
                Port = baseUri.Port
            };
            var forwardRequest = new HttpRequestMessage(request.Method, forwardedUri.Uri);
            if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put)
            {
                var stream = new MemoryStream();
                await request.Content.CopyToAsync(stream);
                stream.Seek(0, SeekOrigin.Begin);
                forwardRequest.Content = new StreamContent(stream);
                //copy the content headers
                foreach (var header in request.Content.Headers)
                {
                    forwardRequest.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
                }
            };
            forwardRequest.Version = request.Version;
            foreach (var prop in request.Properties)
            {
                forwardRequest.Properties.Add(prop);
            }
            foreach (var header in request.Headers)
            {
                forwardRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }
            // Don't forget to change the Host header to refer to the proxy
            forwardRequest.Headers.Host = forwardRequest.RequestUri.Host;
            //Add the relevant X-Forwarded headers
            var xForwardedHost = request.Headers.Host;
            forwardRequest.Headers.Add("X-Forwarded-Host", xForwardedHost);
            var xForwardedFor = HttpContext.Current.Request.UserHostAddress;
            forwardRequest.Headers.Add("X-Forwarded-For", xForwardedFor);
            var client = new HttpClient(new HttpClientHandler(), disposeHandler: false);
            try
            {
                return await client.SendAsync(forwardRequest, HttpCompletionOption.ResponseHeadersRead,
                    cancellationToken);
            }
            catch (Exception e)
            {
                return new HttpResponseMessage(HttpStatusCode.InternalServerError)
                {
                    Content =
                        new ObjectContent<HttpError>(new HttpError(e, includeErrorDetail: true),
                            new JsonMediaTypeFormatter())
                };
            }
        }
    }
}