preface
Let’s talk about the development background of this article first
Under the background of front and back end separation, our customers have requirements again~
To separate the front end from the back end ~ however, for various reasons, there is no way to use a pure front-end framework (in fact, the learning cost is high, and there is no money for front-end developers)
So a plan was finally decided
That is to use MVC (only deal with the front view layer, just for hosting on. Net core) + webapi to realize the separation of the front and back view layers
Then the problem comes
At present, the mainstream front-end frameworks are hosted on nodejs. They access the back-end API through Axios, and cross domain access can be achieved by configuring the proxy configuration (proxytable) of Axios
So our JS runs on MVC and is hosted on. Net core.. what should we do?… there is no ready-made forwarding wheel.. we have to build our own
So this is the background of this article~
text
Fortunately, ASP.NET Core provides us with powerful middleware pattern
We can completely implement the forwarding of proxy interface by defining a forwarding middleware. The process is as follows:
Let’s create our middleware
1、 Interface and implementation of creating detection contract URL
First, we define an interface iurlrewriter to detect whether our URL has a corresponding prefix. If so, a new URL address will be generated
Here we define the interface to facilitate better replacement of the injection class in the future
public interface IUrlRewriter
{
Task<Uri> RewriteUri(HttpContext context);
}
This interface is implemented as follows (explanations are in the comments)
public class PrefixRewriter : IUrlRewriter
{
private readonly PathString _ Prefix; // prefix value
private readonly string _ Newhost; // forwarding address
public PrefixRewriter(PathString prefix, string newHost)
{
_prefix = prefix;
_newHost = newHost;
}
public Task<Uri> RewriteUri(HttpContext context)
{
if ( context.Request.Path .StartsWithSegments(_ Prefix)) // determine whether the access contains a prefix
{
var newUri = context.Request.Path.Value.Remove(0, _prefix.Value.Length) + context.Request.QueryString;
var targetUri = new Uri(_newHost + newUri);
return Task.FromResult(targetUri);
}
return Task.FromResult((Uri)null);
}
}
2、 Create proxyhttpclient for proxy forwarding
The main purpose of creating an independent proxyhttpclient is to distinguish the httpclient forwarded by the proxy, so as to facilitate later log addition or other processing
public class ProxyHttpClient
{
public HttpClient Client { get; private set; }
public ProxyHttpClient(HttpClient httpClient)
{
Client = httpClient;
}
}
3、 Creating middleware for proxy forwarding
The code is as follows. Middleware is mainly the invoke method. You can see the comments for the description
public class ProxyMiddleware
{
// private ProxyHttpClient _proxyHttpClient;
private const string CDN_HEADER_NAME = "Cache-Control";
private static readonly string[] NotForwardedHttpHeaders = new[] { "Connection", "Host" };
private readonly RequestDelegate _next;
private readonly ILogger<ProxyMiddleware> _logger;
public ProxyMiddleware(
RequestDelegate next,
ILogger<ProxyMiddleware> logger
)
{
_next = next;
_logger = logger;
//_proxyHttpClient = proxyHttpClient;
}
/// <summary>
///Through middleware, access is intercepted, prefix is detected and forwarded
/// </summary>
/// <param name="context"></param>
/// <param name="urlRewriter"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context, IUrlRewriter urlRewriter, ProxyHttpClient proxyHttpClient)
{
var targetUri = await urlRewriter.RewriteUri(context);
if (targetUri != null)
{
var requestMessage = GenerateProxifiedRequest(context, targetUri);
await SendAsync(context, requestMessage, proxyHttpClient);
return;
}
await _next(context);
}
private async Task SendAsync(HttpContext context, HttpRequestMessage requestMessage, ProxyHttpClient proxyHttpClient)
{
using (var responseMessage = await proxyHttpClient.Client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
foreach (var header in responseMessage.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
context.Response.Headers.Remove("transfer-encoding");
if (!context.Response.Headers.ContainsKey(CDN_HEADER_NAME))
{
context.Response.Headers.Add(CDN_HEADER_NAME, "no-cache, no-store");
}
await responseMessage.Content.CopyToAsync(context.Response.Body);
}
}
private static HttpRequestMessage GenerateProxifiedRequest(HttpContext context, Uri targetUri)
{
var requestMessage = new HttpRequestMessage();
CopyRequestContentAndHeaders(context, requestMessage);
requestMessage.RequestUri = targetUri;
requestMessage.Headers.Host = targetUri.Host;
requestMessage.Method = GetMethod(context.Request.Method);
return requestMessage;
}
private static void CopyRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage)
{
var requestMethod = context.Request.Method;
if (!HttpMethods.IsGet(requestMethod) &&
!HttpMethods.IsHead(requestMethod) &&
!HttpMethods.IsDelete(requestMethod) &&
!HttpMethods.IsTrace(requestMethod))
{
var streamContent = new StreamContent(context.Request.Body);
requestMessage.Content = streamContent;
}
foreach (var header in context.Request.Headers)
{
if (!NotForwardedHttpHeaders.Contains(header.Key))
{
if (header.Key != "User-Agent")
{
if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && requestMessage.Content != null)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
else
{
string userAgent = header.Value.Count > 0 ? (header.Value[0] + " " + context.TraceIdentifier) : string.Empty;
if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, userAgent) && requestMessage.Content != null)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, userAgent);
}
}
}
}
}
private static HttpMethod GetMethod(string method)
{
if (HttpMethods.IsDelete(method)) return HttpMethod.Delete;
if (HttpMethods.IsGet(method)) return HttpMethod.Get;
if (HttpMethods.IsHead(method)) return HttpMethod.Head;
if (HttpMethods.IsOptions(method)) return HttpMethod.Options;
if (HttpMethods.IsPost(method)) return HttpMethod.Post;
if (HttpMethods.IsPut(method)) return HttpMethod.Put;
if (HttpMethods.IsTrace(method)) return HttpMethod.Trace;
return new HttpMethod(method);
}
4、 Inject and enable our middleware and proxyhttpclient
We add the following code to the configuration services of startup to inject our httpclient and iurlrewriter, as follows:
services.AddHttpClient<ProxyHttpClient>()
.ConfigurePrimaryHttpMessageHandler(x => new HttpClientHandler()
{
AllowAutoRedirect = false,
MaxConnectionsPerServer = int.MaxValue,
UseCookies = false,
}); // inject our defined httpclient
services.AddSingleton <IUrlRewriter>(new PrefixRewriter("/webapp", " http://localhost : 63445 "); // fill in the prefix and the address to forward here
Then, in the configuration of startup, start our middleware as follows:
app.UseMiddleware<ProxyMiddleware>();
5、 Test middleware effect
We write the front-end code as follows:
created: function () {
this.mockTableData1();
axios.get("/webapp/api/values/get", "123").then(res => { alert(res.data[0]) });
axios.post("/webapp/api/values/post",{value: 'david'}).then(res => { alert(res.data.message) });
}
In another webapi project, the interface is as follows:
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", accstring.ToString() };
}
[HttpPost]
public AjaxResult Post(dynamic value)
{
string aaa = JsonConvert.SerializeObject(value);
return Success("OK");
}
The effect is as follows. You can see that our view correctly gets the return value:
Write at the end
Here we implement the proxy forwarding of the interface in the form of middleware, and there will be some small problems in the specific use process, and here we only implement the forwarding of HTTP. WS does not
If you want to use it, there is an open source project abroadhttps://github.com/ProxyKitThere are more than 900 stars. It should be good
This is about ASP.NET This is the article about the implementation of proxy forwarding of core’s ingenious interface, and more related ASP.NET Core interface agent forwarding content, please search previous articles of developer or continue to browse the following related articles. I hope you can support developer more in the future!