You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Framework实现反向代理:全请求转发与自定义请求修改

Hey there! Since you're looking for a .NET Framework-based reverse proxy (no external libraries) to forward all requests from your DMZ web server to your internal one—while supporting all HTTP methods, content types, and allowing request manipulation—I've got you covered.

This solution leans entirely on .NET Framework's built-in HttpWebRequest and ASP.NET pipeline components, perfect for your security architecture where the DMZ server can't directly access the database, but the internal server can.


1. Start with a Lightweight Handler (ASHX)

We'll use an ASP.NET Generic Handler (.ashx) for the proxy logic—it's lightweight and easy to route all requests through. Create a new handler named ProxyHandler.ashx in your .NET Framework web project.

2. Core Proxy Logic Implementation

Here's the full handler code, with comments explaining each step. It handles request forwarding, header/body manipulation, and response propagation:

using System;
using System.IO;
using System.Net;
using System.Web;

public class ProxyHandler : IHttpHandler
{
    // Configure your internal server base URL here (or pull from web.config)
    private readonly string _internalServerBaseUrl = "http://your-internal-web-server-address/";

    public void ProcessRequest(HttpContext context)
    {
        var incomingReq = context.Request;
        var outgoingResp = context.Response;

        // Build the full target URL using the incoming request's path/query
        var targetUrl = $"{_internalServerBaseUrl}{incomingReq.Url.PathAndQuery.TrimStart('/')}";

        // Initialize the request to the internal server
        var targetReq = (HttpWebRequest)WebRequest.Create(targetUrl);
        targetReq.Method = incomingReq.HttpMethod;

        // Copy incoming request headers (skip restricted headers that HttpWebRequest manages)
        foreach (string headerName in incomingReq.Headers)
        {
            if (headerName.Equals("Host", StringComparison.OrdinalIgnoreCase) ||
                headerName.Equals("Content-Length", StringComparison.OrdinalIgnoreCase) ||
                headerName.Equals("Transfer-Encoding", StringComparison.OrdinalIgnoreCase))
            {
                continue;
            }
            targetReq.Headers[headerName] = incomingReq.Headers[headerName];
        }

        // Add custom headers to the forwarded request (example)
        targetReq.Headers.Add("X-DMZ-Proxy", "Forwarded-from-public-server");

        // Handle request body (for POST/PUT/PATCH etc.)
        if (!string.IsNullOrEmpty(incomingReq.ContentType) && incomingReq.ContentLength > 0)
        {
            targetReq.ContentType = incomingReq.ContentType;

            // Optional: Edit the request body here before forwarding
            // Example below shows reading, modifying, and re-writing the body
            using (var incomingStream = incomingReq.InputStream)
            using (var memoryStream = new MemoryStream())
            {
                incomingStream.CopyTo(memoryStream);
                memoryStream.Position = 0;

                // Modify the body (e.g., replace a value in JSON/Form data)
                var bodyContent = new StreamReader(memoryStream).ReadToEnd();
                bodyContent = bodyContent.Replace("placeholder-value", "dmz-modified-value");
                
                // Write the modified body to the target request
                var modifiedBytes = System.Text.Encoding.UTF8.GetBytes(bodyContent);
                targetReq.ContentLength = modifiedBytes.Length;
                using (var targetStream = targetReq.GetRequestStream())
                {
                    targetStream.Write(modifiedBytes, 0, modifiedBytes.Length);
                }
            }
        }

        // Fetch and forward the internal server's response
        try
        {
            using (var targetResp = (HttpWebResponse)targetReq.GetResponse())
            {
                // Propagate status code and headers
                outgoingResp.StatusCode = (int)targetResp.StatusCode;
                foreach (string headerName in targetResp.Headers)
                {
                    outgoingResp.Headers[headerName] = targetResp.Headers[headerName];
                }

                // Propagate response body
                using (var responseStream = targetResp.GetResponseStream())
                {
                    responseStream.CopyTo(outgoingResp.OutputStream);
                }
            }
        }
        catch (WebException ex)
        {
            // Handle errors from the internal server (e.g., 404, 500)
            if (ex.Response is HttpWebResponse errorResp)
            {
                outgoingResp.StatusCode = (int)errorResp.StatusCode;
                using (var errorStream = errorResp.GetResponseStream())
                {
                    errorStream.CopyTo(outgoingResp.OutputStream);
                }
            }
            else
            {
                // Handle connection failures
                outgoingResp.StatusCode = 503;
                outgoingResp.Write("Proxy error: Could not reach internal server.");
            }
        }
    }

    public bool IsReusable => false;
}
3. Route All Requests Through the Proxy

To make the proxy handle every incoming request (instead of just when accessing /ProxyHandler.ashx), add URL rewriting in your web.config or Global.asax.

Option 1: Web.config URL Rewrite

Add this to your <system.webServer> section to rewrite all non-file/directory requests to the proxy handler:

<system.webServer>
  <rewrite>
    <rules>
      <rule name="DMZ Reverse Proxy" stopProcessing="true">
        <match url="^(.*)$" />
        <conditions>
          <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
          <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
        </conditions>
        <action type="Rewrite" url="ProxyHandler.ashx/{R:1}" />
      </rule>
    </rules>
  </rewrite>
</system.webServer>

Option 2: Global.asax Routing

Add this to your Global.asax.cs Application_BeginRequest method:

void Application_BeginRequest(object sender, EventArgs e)
{
    var context = HttpContext.Current;
    var requestPath = context.Request.Url.LocalPath;

    // Skip static files (adjust extensions as needed)
    if (!requestPath.StartsWith("/ProxyHandler.ashx") && 
        !requestPath.EndsWith(".css") && 
        !requestPath.EndsWith(".js") && 
        !requestPath.EndsWith(".png"))
    {
        context.RewritePath("~/ProxyHandler.ashx");
    }
}
4. Key Features & Notes
  • Full HTTP Method Support: The code forwards the original request method (GET, POST, PUT, DELETE, PATCH, etc.) directly to the internal server.
  • All Content-Types: Works with JSON, form data, binary files, and more—since we're streaming the request/response bodies directly.
  • Request Manipulation: Customize headers or modify the request body (as shown in the code comment) to add security tokens, sanitize data, or inject context.
  • Security: Restrict access to the proxy via IP whitelisting (add checks in ProcessRequest), and filter sensitive headers if needed (e.g., skip forwarding Authorization unless explicitly required).
  • Performance: Adjust ServicePointManager.DefaultConnectionLimit in your application startup to handle high concurrency (default is low for .NET Framework).

内容的提问来源于stack exchange,提问作者Ogglas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:22:21