使用 ASP.NET C# 重写 HOST
Rewrite HOST using ASP.NET C#
我正在尝试重定向 HOST 而不是 URL 的路径,因此外部站点可以访问内部 API 资源。为简单起见,对于此示例,主机将根据 header.
进行更改
目前,我只是 运行 Visual Studio。地址将是:
http://externalsite.company.com/testapi/myapi.asmx/GetNewKey
我要改成:
http://internalsite1.local/testapi/myapi.asmx/GetNewKey
基于 header "hostingAuth" 中的值。
将有 headers 和 body 到达页面,"internalsiteX.local" 服务器将使用该页面。这将因公司而异,因此我无法解释所有可能性。
目前,我的安全令牌是 header "hostingAuth" 而下面的示例中,唯一有效的令牌是 "company1secret" 和 "company2secret"
我想我想要的是重写模块,但这需要我在 web.config (Intelligencia.UrlRewriter.RewriterHttpModule) 中静态编码 rewrite/redirects。将有数百个条目,所以我不想要静态文件,我想使用数据库以便可以通过代码更改它。我不能使用(也许?)IIS ARR add-in,因为我需要通过安全令牌保护公司。
我正在寻找类似的东西,除了 "urlRequestContext.Request.Url.Host" 只是 GET 而不是 SET
Global.asax.cs:
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext urlRequestContext = HttpContext.Current;
if (!(urlRequestContext.Request.Url.AbsolutePath.ToLower().StartsWith("/errorpages/")))
{
try
{
string hostingAuth = urlRequestContext.Request.Headers.GetValues("hostingAuth").FirstOrDefault();
if (hostingAuth == "company1secret")
{
urlRequestContext.Request.Url.Host = "internalsite1.local";
}
if (hostingAuth == "company2secret")
{
urlRequestContext.Request.Url.Host = "internalsite2.local";
}
}
catch (Exception ex)
{
Response.Redirect("/errorpages/missingtoken.aspx", true);
}
}
}
我找不到有关如何执行此操作的示例。它要么非常简单,不值得任何示例,要么不可能。有没有人有什么建议?我是不是完全用错误的方式来处理这个问题?
谢谢
事实证明这不是一个简单的解决方案,而且您没有更改主机。
ARR 几乎有点独创性,通过将主机 header 写为服务器名称和域,除了 URL-rewrite 第一次工作,但在应用程序第二次运行时被忽略将信息发送回内部服务器。
只有一个反向代理解决方案接近,它是:
https://gist.github.com/anth-3/6169292
这是代码的基础。基本思路是:
- 向应用程序的 URL 发出请求 (http://myexternalserver.validdomain.com?someparameter1=foo&someparameter2=bar)
- 应用程序读取 URI、headers 和 body
- 该应用使用完全相同的 header 信息(或足够接近)向新主机发出请求 (http://company2internal.validdomain.com?someparameter1=foo&someparameter2=bar)
- 读取结果并将其发送回原始请求者,并将来自内部机器的 headers 添加到响应中。
为了完整起见,包含了代码。这是一些草稿,但还没有完善或 QA'd,所以预计会有错误。它似乎适用于所有动词 *1 ,但主要使用 GET 进行测试,这也适用于 https *2
*1 如果你想让 PUT、DELETE 和其他动词起作用,你需要在 IIS 中启用它,看起来你也需要在 web.config
中启用它
*2 要使 https 正常工作,外部网站需要有效的证书才能正常工作。这已通过 https://443
在外部和内部网站上使用通配符证书进行了测试
这为我完成了初始 POC 的工作。
空白项目中仅有的页面是:
global.asax
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext urlRequestContext = HttpContext.Current;
if (!(urlRequestContext.Request.Url.AbsolutePath.ToLower().StartsWith("/errorpages/")))
{
try
{
string hostingAuth = urlRequestContext.Request.Headers.GetValues("hostingauth").FirstOrDefault();
if (hostingAuth == "company2secret")
{
string newHost = "company2internal.validdomain.com";
reverseProxy.ProcessRequest(urlRequestContext, newHost);
}
}
catch (System.Threading.ThreadAbortException)
{
// ignore it
}
catch (Exception ex)
{
Response.Redirect("/errorpages/missingtoken.aspx", true);
}
}
}
.
class1.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
namespace redirect
{
public class reverseProxy
{
//public static bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
//{
// return true;
//}
public static void ProcessRequest(HttpContext Context, string newHost)
{
//ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
/* Create variables to hold the request and response. */
HttpRequest Request = Context.Request;
HttpResponse Response = Context.Response;
string URI = null;
URI = Request.Url.Scheme.ToString() + "://" + newHost + Request.Url.PathAndQuery;
/* Create an HttpWebRequest to send the URI on and process results. */
System.Net.HttpWebRequest ProxyRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(URI);
/* Set the same requests to our request as in the incoming request */
ProxyRequest.Method = Request.HttpMethod.ToUpper();
ProxyRequest.ServicePoint.Expect100Continue = false;
ProxyRequest.Accept = Request.Headers["Accept"];
//ProxyRequest.TransferEncoding = Request.Headers["Accept-encoding"];
ProxyRequest.SendChunked = false;
//ProxyRequest.Date = Request.Headers["Date"];
ProxyRequest.Expect = Request.Headers["Expect"];
//ProxyRequest.IfModifiedSince = Request.Headers["If-Modified-Since"];
//ProxyRequest.Range = Request.Headers["Range"];
ProxyRequest.Referer = Request.Headers["Referer"];
ProxyRequest.TransferEncoding = Request.Headers["Transfer-Encoding"];
ProxyRequest.UserAgent = Request.Headers["User-Agent"];
//set the same headers except for certain ones as they need to be set not in this way
foreach (string strKey in Request.Headers.AllKeys)
{
if ((strKey != "Accept") && (strKey != "Connection") && (strKey != "Content-Length") && (strKey != "Content-Type") && (strKey != "Date") && (strKey != "Expect") && (strKey != "Host") && (strKey != "If-Modified-Since") && (strKey != "Range") && (strKey != "Referer") && (strKey != "Transfer-Encoding") && (strKey != "User-Agent") && (strKey != "Proxy-Connection") && (strKey != "hostingauth"))
ProxyRequest.Headers.Add(strKey, Request.Headers[strKey]);
}
if (Request.InputStream.Length > 0)
{
/*
* Since we are using the same request method as the original request, and that is
* a POST, the values to send on in the new request must be grabbed from the
* original POSTed request.
*/
byte[] Bytes = new byte[Request.InputStream.Length];
Request.InputStream.Read(Bytes, 0, (int)Request.InputStream.Length);
ProxyRequest.ContentLength = Bytes.Length;
string ContentType = Request.ContentType;
if (String.IsNullOrEmpty(ContentType))
{
ProxyRequest.ContentType = "application/x-www-form-urlencoded";
}
else
{
ProxyRequest.ContentType = ContentType;
}
using (Stream OutputStream = ProxyRequest.GetRequestStream())
{
OutputStream.Write(Bytes, 0, Bytes.Length);
}
}
//else
//{
// /*
// * When the original request is a GET, things are much easier, as we need only to
// * pass the URI we collected earlier which will still have any parameters
// * associated with the request attached to it.
// */
// //ProxyRequest.Method = "GET";
//}
System.Net.WebResponse ServerResponse = null;
/* Send the proxy request to the remote server or fail. */
try
{
//even if it isn't gzipped it tries but ignores if it fails
ProxyRequest.AutomaticDecompression = DecompressionMethods.GZip;
ServerResponse = ProxyRequest.GetResponse();
}
catch (System.Net.WebException WebEx)
{
#region exceptionError
Response.StatusCode = 500;
Response.StatusDescription = WebEx.Status.ToString();
Response.Write(WebEx.Message);
Response.Write("\r\n");
Response.Write(((System.Net.HttpWebResponse)WebEx.Response).ResponseUri);
Response.Write("\r\n");
Response.Write(((System.Net.HttpWebResponse)WebEx.Response).Method);
Response.Write("\r\n");
Response.Write("Headers\r\n");
foreach (string strKey in Request.Headers.AllKeys)
{
Response.Write(strKey + ": " +Request.Headers[strKey]);
Response.Write("\r\n");
}
Response.End();
#endregion
return;
}
/* Set up the response to the client if there is one to set up. */
if (ServerResponse != null)
{
Response.ContentType = ServerResponse.ContentType;
using (Stream ByteStream = ServerResponse.GetResponseStream())
{
/* What is the response type? */
if (ServerResponse.ContentType.Contains("text") ||
ServerResponse.ContentType.Contains("json") ||
ServerResponse.ContentType.Contains("xml"))
{
/* These "text" types are easy to handle. */
using (StreamReader Reader = new StreamReader(ByteStream))
{
string ResponseString = Reader.ReadToEnd();
/*
* Tell the client not to cache the response since it
* could easily be dynamic, and we do not want to mess
* that up!
*/
Response.CacheControl = "no-cache";
//If the request came with a gzip request, send it back gzipped
if (Request.Headers["Accept-encoding"].Contains("gzip"))
{
Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.AppendHeader("Content-Encoding", "gzip");
}
//write webpage/results back
Response.Write(ResponseString);
}
}
else
{
//This is completely untested
/*
* Handle binary responses (image, layer file, other binary
* files) differently than text.
*/
BinaryReader BinReader = new BinaryReader(ByteStream);
byte[] BinaryOutputs = BinReader.ReadBytes((int)ServerResponse.ContentLength);
BinReader.Close();
/*
* Tell the client not to cache the response since it could
* easily be dynamic, and we do not want to mess that up!
*/
Response.CacheControl = "no-cache";
//could this make it more efficient - untested
if (Request.Headers["Accept-encoding"].Contains("gzip"))
{
Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.AppendHeader("Content-Encoding", "gzip");
}
/*
* Send the binary response to the client.
* (Note: if large images/files are sent, we could modify this to
* send back in chunks instead...something to think about for
* future.)
*/
Response.OutputStream.Write(BinaryOutputs, 0, BinaryOutputs.Length);
}
ServerResponse.Close();
}
}
//done
Response.End();
}
}
}
.
在 web.config 中,这显示了 PUT 和 DELETE 的动词覆盖起作用。我知道我会在没有扩展名的情况下反向代理 aspx 和 api。这可能是矫枉过正且效率低下,但它有效
web.config
<!--
For more information on how to configure your ASP.NET application, please visit
https://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.6.1"/>
<httpRuntime targetFramework="4.6.1"/>
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701"/>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"/>
</compilers>
</system.codedom>
<system.webServer>
<handlers>
<remove name="PageHandlerFactory-ISAPI-2.0-64" />
<remove name="PageHandlerFactory-ISAPI-2.0" />
<remove name="PageHandlerFactory-Integrated-4.0" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="PageHandlerFactory-ISAPI-4.0_32bit" />
<remove name="PageHandlerFactory-ISAPI-4.0_64bit" />
<remove name="PageHandlerFactory-Integrated" />
<add name="PageHandlerFactory-Integrated" path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="PageHandlerFactory-ISAPI-4.0_64bit" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-4.0_32bit" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="PageHandlerFactory-Integrated-4.0" path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="PageHandlerFactory-ISAPI-2.0" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-2.0-64" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
</handlers>
</system.webServer>
</configuration>
/errorpages/missingtoken.aspx
<body>
the token is missing
</body>
如果您想知道速度,Postman 给了我
的时间
- 350-600 毫秒直接通过 Internet 呈现 .aspx 页面。
- 600-900ms 通过反向代理(2 个不同的位置。RP 在家里,内部服务器(在外部呈现)在现场)。我怀疑这是由于多次 unGzipping 和 reGzipping 造成的。
- 800ms-1s 如果它通过 RP,但没有完成 GZIPping。
如果我不用解压重新压缩的话,估计时间会更接近直接时间。
如果我可以在现场部署 RP 并从内部服务器请求它解压缩并以压缩格式返回,这可能也会更快。 YMMV
.
如果您想知道为什么要麻烦 - 这是将身份验证放入通常位于 IP 白名单后面的 API 请求中。 RP 不会被列入白名单,我无权访问 API 代码来更改它。
我正在尝试重定向 HOST 而不是 URL 的路径,因此外部站点可以访问内部 API 资源。为简单起见,对于此示例,主机将根据 header.
进行更改目前,我只是 运行 Visual Studio。地址将是:
http://externalsite.company.com/testapi/myapi.asmx/GetNewKey
我要改成:
http://internalsite1.local/testapi/myapi.asmx/GetNewKey
基于 header "hostingAuth" 中的值。
将有 headers 和 body 到达页面,"internalsiteX.local" 服务器将使用该页面。这将因公司而异,因此我无法解释所有可能性。
目前,我的安全令牌是 header "hostingAuth" 而下面的示例中,唯一有效的令牌是 "company1secret" 和 "company2secret"
我想我想要的是重写模块,但这需要我在 web.config (Intelligencia.UrlRewriter.RewriterHttpModule) 中静态编码 rewrite/redirects。将有数百个条目,所以我不想要静态文件,我想使用数据库以便可以通过代码更改它。我不能使用(也许?)IIS ARR add-in,因为我需要通过安全令牌保护公司。
我正在寻找类似的东西,除了 "urlRequestContext.Request.Url.Host" 只是 GET 而不是 SET
Global.asax.cs:
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext urlRequestContext = HttpContext.Current;
if (!(urlRequestContext.Request.Url.AbsolutePath.ToLower().StartsWith("/errorpages/")))
{
try
{
string hostingAuth = urlRequestContext.Request.Headers.GetValues("hostingAuth").FirstOrDefault();
if (hostingAuth == "company1secret")
{
urlRequestContext.Request.Url.Host = "internalsite1.local";
}
if (hostingAuth == "company2secret")
{
urlRequestContext.Request.Url.Host = "internalsite2.local";
}
}
catch (Exception ex)
{
Response.Redirect("/errorpages/missingtoken.aspx", true);
}
}
}
我找不到有关如何执行此操作的示例。它要么非常简单,不值得任何示例,要么不可能。有没有人有什么建议?我是不是完全用错误的方式来处理这个问题?
谢谢
事实证明这不是一个简单的解决方案,而且您没有更改主机。
ARR 几乎有点独创性,通过将主机 header 写为服务器名称和域,除了 URL-rewrite 第一次工作,但在应用程序第二次运行时被忽略将信息发送回内部服务器。
只有一个反向代理解决方案接近,它是: https://gist.github.com/anth-3/6169292
这是代码的基础。基本思路是:
- 向应用程序的 URL 发出请求 (http://myexternalserver.validdomain.com?someparameter1=foo&someparameter2=bar)
- 应用程序读取 URI、headers 和 body
- 该应用使用完全相同的 header 信息(或足够接近)向新主机发出请求 (http://company2internal.validdomain.com?someparameter1=foo&someparameter2=bar)
- 读取结果并将其发送回原始请求者,并将来自内部机器的 headers 添加到响应中。
为了完整起见,包含了代码。这是一些草稿,但还没有完善或 QA'd,所以预计会有错误。它似乎适用于所有动词 *1 ,但主要使用 GET 进行测试,这也适用于 https *2
*1 如果你想让 PUT、DELETE 和其他动词起作用,你需要在 IIS 中启用它,看起来你也需要在 web.config
中启用它*2 要使 https 正常工作,外部网站需要有效的证书才能正常工作。这已通过 https://443
在外部和内部网站上使用通配符证书进行了测试这为我完成了初始 POC 的工作。
空白项目中仅有的页面是:
global.asax
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext urlRequestContext = HttpContext.Current;
if (!(urlRequestContext.Request.Url.AbsolutePath.ToLower().StartsWith("/errorpages/")))
{
try
{
string hostingAuth = urlRequestContext.Request.Headers.GetValues("hostingauth").FirstOrDefault();
if (hostingAuth == "company2secret")
{
string newHost = "company2internal.validdomain.com";
reverseProxy.ProcessRequest(urlRequestContext, newHost);
}
}
catch (System.Threading.ThreadAbortException)
{
// ignore it
}
catch (Exception ex)
{
Response.Redirect("/errorpages/missingtoken.aspx", true);
}
}
}
.
class1.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
namespace redirect
{
public class reverseProxy
{
//public static bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
//{
// return true;
//}
public static void ProcessRequest(HttpContext Context, string newHost)
{
//ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
/* Create variables to hold the request and response. */
HttpRequest Request = Context.Request;
HttpResponse Response = Context.Response;
string URI = null;
URI = Request.Url.Scheme.ToString() + "://" + newHost + Request.Url.PathAndQuery;
/* Create an HttpWebRequest to send the URI on and process results. */
System.Net.HttpWebRequest ProxyRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(URI);
/* Set the same requests to our request as in the incoming request */
ProxyRequest.Method = Request.HttpMethod.ToUpper();
ProxyRequest.ServicePoint.Expect100Continue = false;
ProxyRequest.Accept = Request.Headers["Accept"];
//ProxyRequest.TransferEncoding = Request.Headers["Accept-encoding"];
ProxyRequest.SendChunked = false;
//ProxyRequest.Date = Request.Headers["Date"];
ProxyRequest.Expect = Request.Headers["Expect"];
//ProxyRequest.IfModifiedSince = Request.Headers["If-Modified-Since"];
//ProxyRequest.Range = Request.Headers["Range"];
ProxyRequest.Referer = Request.Headers["Referer"];
ProxyRequest.TransferEncoding = Request.Headers["Transfer-Encoding"];
ProxyRequest.UserAgent = Request.Headers["User-Agent"];
//set the same headers except for certain ones as they need to be set not in this way
foreach (string strKey in Request.Headers.AllKeys)
{
if ((strKey != "Accept") && (strKey != "Connection") && (strKey != "Content-Length") && (strKey != "Content-Type") && (strKey != "Date") && (strKey != "Expect") && (strKey != "Host") && (strKey != "If-Modified-Since") && (strKey != "Range") && (strKey != "Referer") && (strKey != "Transfer-Encoding") && (strKey != "User-Agent") && (strKey != "Proxy-Connection") && (strKey != "hostingauth"))
ProxyRequest.Headers.Add(strKey, Request.Headers[strKey]);
}
if (Request.InputStream.Length > 0)
{
/*
* Since we are using the same request method as the original request, and that is
* a POST, the values to send on in the new request must be grabbed from the
* original POSTed request.
*/
byte[] Bytes = new byte[Request.InputStream.Length];
Request.InputStream.Read(Bytes, 0, (int)Request.InputStream.Length);
ProxyRequest.ContentLength = Bytes.Length;
string ContentType = Request.ContentType;
if (String.IsNullOrEmpty(ContentType))
{
ProxyRequest.ContentType = "application/x-www-form-urlencoded";
}
else
{
ProxyRequest.ContentType = ContentType;
}
using (Stream OutputStream = ProxyRequest.GetRequestStream())
{
OutputStream.Write(Bytes, 0, Bytes.Length);
}
}
//else
//{
// /*
// * When the original request is a GET, things are much easier, as we need only to
// * pass the URI we collected earlier which will still have any parameters
// * associated with the request attached to it.
// */
// //ProxyRequest.Method = "GET";
//}
System.Net.WebResponse ServerResponse = null;
/* Send the proxy request to the remote server or fail. */
try
{
//even if it isn't gzipped it tries but ignores if it fails
ProxyRequest.AutomaticDecompression = DecompressionMethods.GZip;
ServerResponse = ProxyRequest.GetResponse();
}
catch (System.Net.WebException WebEx)
{
#region exceptionError
Response.StatusCode = 500;
Response.StatusDescription = WebEx.Status.ToString();
Response.Write(WebEx.Message);
Response.Write("\r\n");
Response.Write(((System.Net.HttpWebResponse)WebEx.Response).ResponseUri);
Response.Write("\r\n");
Response.Write(((System.Net.HttpWebResponse)WebEx.Response).Method);
Response.Write("\r\n");
Response.Write("Headers\r\n");
foreach (string strKey in Request.Headers.AllKeys)
{
Response.Write(strKey + ": " +Request.Headers[strKey]);
Response.Write("\r\n");
}
Response.End();
#endregion
return;
}
/* Set up the response to the client if there is one to set up. */
if (ServerResponse != null)
{
Response.ContentType = ServerResponse.ContentType;
using (Stream ByteStream = ServerResponse.GetResponseStream())
{
/* What is the response type? */
if (ServerResponse.ContentType.Contains("text") ||
ServerResponse.ContentType.Contains("json") ||
ServerResponse.ContentType.Contains("xml"))
{
/* These "text" types are easy to handle. */
using (StreamReader Reader = new StreamReader(ByteStream))
{
string ResponseString = Reader.ReadToEnd();
/*
* Tell the client not to cache the response since it
* could easily be dynamic, and we do not want to mess
* that up!
*/
Response.CacheControl = "no-cache";
//If the request came with a gzip request, send it back gzipped
if (Request.Headers["Accept-encoding"].Contains("gzip"))
{
Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.AppendHeader("Content-Encoding", "gzip");
}
//write webpage/results back
Response.Write(ResponseString);
}
}
else
{
//This is completely untested
/*
* Handle binary responses (image, layer file, other binary
* files) differently than text.
*/
BinaryReader BinReader = new BinaryReader(ByteStream);
byte[] BinaryOutputs = BinReader.ReadBytes((int)ServerResponse.ContentLength);
BinReader.Close();
/*
* Tell the client not to cache the response since it could
* easily be dynamic, and we do not want to mess that up!
*/
Response.CacheControl = "no-cache";
//could this make it more efficient - untested
if (Request.Headers["Accept-encoding"].Contains("gzip"))
{
Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.AppendHeader("Content-Encoding", "gzip");
}
/*
* Send the binary response to the client.
* (Note: if large images/files are sent, we could modify this to
* send back in chunks instead...something to think about for
* future.)
*/
Response.OutputStream.Write(BinaryOutputs, 0, BinaryOutputs.Length);
}
ServerResponse.Close();
}
}
//done
Response.End();
}
}
}
.
在 web.config 中,这显示了 PUT 和 DELETE 的动词覆盖起作用。我知道我会在没有扩展名的情况下反向代理 aspx 和 api。这可能是矫枉过正且效率低下,但它有效
web.config
<!--
For more information on how to configure your ASP.NET application, please visit
https://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.6.1"/>
<httpRuntime targetFramework="4.6.1"/>
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701"/>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"/>
</compilers>
</system.codedom>
<system.webServer>
<handlers>
<remove name="PageHandlerFactory-ISAPI-2.0-64" />
<remove name="PageHandlerFactory-ISAPI-2.0" />
<remove name="PageHandlerFactory-Integrated-4.0" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="PageHandlerFactory-ISAPI-4.0_32bit" />
<remove name="PageHandlerFactory-ISAPI-4.0_64bit" />
<remove name="PageHandlerFactory-Integrated" />
<add name="PageHandlerFactory-Integrated" path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="PageHandlerFactory-ISAPI-4.0_64bit" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-4.0_32bit" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="PageHandlerFactory-Integrated-4.0" path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="PageHandlerFactory-ISAPI-2.0" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-2.0-64" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
</handlers>
</system.webServer>
</configuration>
/errorpages/missingtoken.aspx
<body>
the token is missing
</body>
如果您想知道速度,Postman 给了我
的时间- 350-600 毫秒直接通过 Internet 呈现 .aspx 页面。
- 600-900ms 通过反向代理(2 个不同的位置。RP 在家里,内部服务器(在外部呈现)在现场)。我怀疑这是由于多次 unGzipping 和 reGzipping 造成的。
- 800ms-1s 如果它通过 RP,但没有完成 GZIPping。
如果我不用解压重新压缩的话,估计时间会更接近直接时间。
如果我可以在现场部署 RP 并从内部服务器请求它解压缩并以压缩格式返回,这可能也会更快。 YMMV
.
如果您想知道为什么要麻烦 - 这是将身份验证放入通常位于 IP 白名单后面的 API 请求中。 RP 不会被列入白名单,我无权访问 API 代码来更改它。