前言
先讲讲本文的开发背景吧..
在如今前后端分离的大背景下,咱的客户又有要求啦~
要前后端分离~ 然因为种种原因..没办法用用纯前端的框架(其实是学习成本高,又没钱请前端开发人员)...
所以最终决定了一种方案..
那就是采用MVC(只处理前端视图层,单纯是为了托管在.net core上)+Webapi的方式来实现前后端分离(讲真,很奇葩)..
那么问题就随之而来了.
现在主流的前端框架都是托管在nodejs上,是通过axios来访问后端API,可以通过配置axios的代理配置(proxyTable)来实现跨域访问.
那么我们的JS运行在MVC上,托管在.net core上..那咋办呢"color: #ff0000">正文
幸运的是ASP.NET Core 给我们提供了强大的中间件模式.
我们完全可以通过定义一个转发中间件的形式来实现代理接口转发,流程如图:
废话不多说,我们来创建我们的中间件:
一.创建检测约定URL的接口与实现
首先定义一个接口IUrlRewriter 用来检测我们的URL是否有对应前缀,如果有,则产生新的URL地址:
这里我们定义接口是为了方便以后更好的更换注入类来实现快速更换检测前缀的规则.
public interface IUrlRewriter { Task<Uri> RewriteUri(HttpContext context); }
实现这个接口,如下(解释都在注释里了):
public class PrefixRewriter : IUrlRewriter { private readonly PathString _prefix; //前缀值 private readonly string _newHost; //转发的地址 public PrefixRewriter(PathString prefix, string newHost) { _prefix = prefix; _newHost = newHost; } public Task<Uri> 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); } }
二.创建代理转发需要的ProxyHttpClient
创建独立的ProxyHttpClient,主要是为了区分代理转发的httpClient,方便后期添加日志或做别的处理.代码如下:
public class ProxyHttpClient { public HttpClient Client { get; private set; } public ProxyHttpClient(HttpClient httpClient) { Client = httpClient; } }
三.创建代理转发的中间件
代码如下,中间件嘛,主要就是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<ProxyMiddleware> _logger; public ProxyMiddleware( RequestDelegate next, ILogger<ProxyMiddleware> logger ) { _next = next; _logger = logger; //_proxyHttpClient = proxyHttpClient; } /// <summary> /// 通过中间件,拦截访问,检测前缀,并转发 /// </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" " + context.TraceIdentifier) : string.Empty; if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, userAgent) && requestMessage.Content != null) { requestMessage.Content"color: #ff0000">四.注入和启用我们的中间件和ProxyHttpClient我们在Startup的ConfigureServices中添加如下代码,注入我们的HttpClient与IUrlRewriter,如下:
services.AddHttpClient<ProxyHttpClient>() .ConfigurePrimaryHttpMessageHandler(x => new HttpClientHandler() { AllowAutoRedirect = false, MaxConnectionsPerServer = int.MaxValue, UseCookies = false, }); //注入我们定义的HttpClient services.AddSingleton<IUrlRewriter>(new PrefixRewriter("/webapp", "http://localhost:63445"));//这里填写前缀与需要转发的地址然后在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 ActionResult<IEnumerable<string Get() { return new string[] { "value1", accstring.ToString() }; } [HttpPost] public AjaxResult Post(dynamic value) { string aaa = JsonConvert.SerializeObject(value); return Success("OK"); }效果如下,可以看到我们的视图正确的获取到了返回值:
写在最后
这里我们通过中间件的形式实现了接口的代理转发,在具体的使用过程中肯定还会有一些小问题,而且这里我们只实现了Http的转发.ws的则没有.
如果要使用的话,其实国外有一个开源的项目:https://github.com/ProxyKit, 已经有900多个star了.应该还不错.
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。