<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ā)

          共 7331字,需瀏覽 15分鐘

           ·

          2020-09-20 19:13

          轉(zhuǎn)自:GuZhenYin
          cnblogs.com/GuZhenYin/p/13477075.html

          前言


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


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


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


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


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


          那么問題就隨之而來了.


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


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


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


          正文


          幸運(yùn)的是ASP.NET Core 給我們提供了強(qiáng)大的中間件模式.


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



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


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


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


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


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


          實(shí)現(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)建獨(dú)立的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)
          {
          _next = next;
          _logger = logger;
          //_proxyHttpClient = proxyHttpClient;
          }
          ///
          /// 通過中間件,攔截訪問,檢測前綴,并轉(zhuǎn)發(fā)
          ///

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


          四、注入和啟用我們的中間件和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項(xiàng)目,編寫接口如下:


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


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



          寫在最后


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


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


          瀏覽 53
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  黄色免费操逼视频 | 黄色电影网站在线免费观看 | 亚洲每日在线 | 超碰免费自拍 | 日韩福利一区二区三区 |