<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ASP.NET Core 奇技淫巧之接口代理轉(zhuǎn)發(fā)

          共 8067字,需瀏覽 17分鐘

           ·

          2020-08-16 06:23

          前言

          先講講本文的開發(fā)背景吧..

          在如今前后端分離的大背景下,咱的客戶又有要求啦~

          要前后端分離~ 然因為種種原因..沒辦法用用純前端的框架(其實是學(xué)習(xí)成本高,又沒錢請前端開發(fā)人員)...

          所以最終決定了一種方案..

          那就是采用MVC(只處理前端視圖層,單純是為了托管在.net core上)+Webapi的方式來實現(xiàn)前后端分離(講真,很奇葩)..

          那么問題就隨之而來了.

          現(xiàn)在主流的前端框架都是托管在nodejs上,是通過axios來訪問后端API,可以通過配置axios的代理配置(proxyTable)來實現(xiàn)跨域訪問.

          那么我們的JS運行在MVC上,托管在.net core上..那咋辦呢?..沒有現(xiàn)成的轉(zhuǎn)發(fā)輪子..我們只有自己造了..

          所以這就是本篇的背景 - -.~

          ?

          正文

          幸運的是ASP.NET Core 給我們提供了強大的中間件模式.

          我們完全可以通過定義一個轉(zhuǎn)發(fā)中間件的形式來實現(xiàn)代理接口轉(zhuǎn)發(fā),流程如圖:

          廢話不多說,我們來創(chuàng)建我們的中間件:

          ?

          一.創(chuàng)建檢測約定URL的接口與實現(xiàn)

          首先定義一個接口IUrlRewriter 用來檢測我們的URL是否有對應(yīng)前綴,如果有,則產(chǎn)生新的URL地址:

          這里我們定義接口是為了方便以后更好的更換注入類來實現(xiàn)快速更換檢測前綴的規(guī)則.

          public interface IUrlRewriter{        Task RewriteUri(HttpContext context);}

          實現(xiàn)這個接口,如下(解釋都在注釋里了):

              public class PrefixRewriter : IUrlRewriter    {        private readonly PathString _prefix; //前綴值        private readonly string _newHost; //轉(zhuǎn)發(fā)的地址
          public PrefixRewriter(PathString prefix, string newHost) { _prefix = prefix; _newHost = newHost; }
          public Task RewriteUri(HttpContext context) { if (context.Request.Path.StartsWithSegments(_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); } }

          ?

          二.創(chuàng)建代理轉(zhuǎn)發(fā)需要的ProxyHttpClient

          創(chuàng)建獨立的ProxyHttpClient,主要是為了區(qū)分代理轉(zhuǎn)發(fā)的httpClient,方便后期添加日志或做別的處理.代碼如下:

              public class ProxyHttpClient    {        public HttpClient Client { get; private set; }
          public ProxyHttpClient(HttpClient httpClient) { Client = httpClient; } }


          ?

          三.創(chuàng)建代理轉(zhuǎn)發(fā)的中間件

          代碼如下,中間件嘛,主要就是Invoke方法了,說明可以看注釋.

            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 _logger;
          public ProxyMiddleware( RequestDelegate next, ILogger logger, ProxyHttpClient proxyHttpClient) { _next = next; _logger = logger; _proxyHttpClient = proxyHttpClient; }
          /// /// 通過中間件,攔截訪問,檢測前綴,并轉(zhuǎn)發(fā) /// /// /// /// public async Task Invoke(HttpContext context, IUrlRewriter urlRewriter) { var targetUri = await urlRewriter.RewriteUri(context);
          if (targetUri != null) { var requestMessage = GenerateProxifiedRequest(context, targetUri); await SendAsync(context, requestMessage);
          return; }
          await _next(context); }
          private async Task SendAsync(HttpContext context, HttpRequestMessage requestMessage) {
          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); } }

          ?

          四.注入和啟用我們的中間件和ProxyHttpClient

          我們在Startup的ConfigureServices中添加如下代碼,注入我們的HttpClient與IUrlRewriter,如下:


          services.AddHttpClient() .ConfigurePrimaryHttpMessageHandler(x => new HttpClientHandler() { AllowAutoRedirect = false, MaxConnectionsPerServer = int.MaxValue, UseCookies = false, }); //注入我們定義的HttpClient services.AddSingleton(new PrefixRewriter("/webapp", "http://localhost:63445"));//這里填寫前綴與需要轉(zhuǎn)發(fā)的地址


          然后在Startup的Configure中,啟動我們的中間件,如下:

            app.UseMiddleware<ProxyMiddleware>();

          五.測試中間件效果

          我們編寫前端代碼如下:

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


          在另外的WebApi項目,編寫接口如下:

                  [HttpGet]        public ActionResultstring>> Get()        {            return new string[] { "value1", accstring.ToString() };        }
          [HttpPost] public AjaxResult Post(dynamic value) { string aaa = JsonConvert.SerializeObject(value); return Success("OK"); }


          效果如下,可以看到我們的視圖正確的獲取到了返回值:

          ?

          ?

          寫在最后

          這里我們通過中間件的形式實現(xiàn)了接口的代理轉(zhuǎn)發(fā),在具體的使用過程中肯定還會有一些小問題,而且這里我們只實現(xiàn)了Http的轉(zhuǎn)發(fā).ws的則沒有.

          如果要使用的話,其實國外有一個開源的項目:https://github.com/ProxyKit, 已經(jīng)有900多個star了.應(yīng)該還不錯.

          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  激情五月天色青五月天 | 青青草视频免费 | 一级欧美性爱视频 | 亚洲一级性爱 | 国产成人精品一区二区三区四区五区 |