ASP.NET Core 奇技淫巧之接口代理转发的实现
前言
先讲讲本文的开发背景吧..
在如今前后端分离的大背景下,咱的客户又有要求啦~
要前后端分离~然因为种种原因..没办法用用纯前端的框架(其实是学习成本高,又没钱请前端开发人员)...
所以最终决定了一种方案..
那就是采用MVC(只处理前端视图层,单纯是为了托管在.netcore上)+Webapi的方式来实现前后端分离(讲真,很奇葩)..
那么问题就随之而来了.
现在主流的前端框架都是托管在nodejs上,是通过axios来访问后端API,可以通过配置axios的代理配置(proxyTable)来实现跨域访问.
那么我们的JS运行在MVC上,托管在.netcore上..那咋办呢?..没有现成的转发轮子..我们只有自己造了..
所以这就是本篇的背景--.~
正文
幸运的是ASP.NETCore给我们提供了强大的中间件模式.
我们完全可以通过定义一个转发中间件的形式来实现代理接口转发,流程如图:
废话不多说,我们来创建我们的中间件:
一.创建检测约定URL的接口与实现
首先定义一个接口IUrlRewriter用来检测我们的URL是否有对应前缀,如果有,则产生新的URL地址:
这里我们定义接口是为了方便以后更好的更换注入类来实现快速更换检测前缀的规则.
publicinterfaceIUrlRewriter { TaskRewriteUri(HttpContextcontext); }
实现这个接口,如下(解释都在注释里了):
publicclassPrefixRewriter:IUrlRewriter { privatereadonlyPathString_prefix;//前缀值 privatereadonlystring_newHost;//转发的地址 publicPrefixRewriter(PathStringprefix,stringnewHost) { _prefix=prefix; _newHost=newHost; } publicTaskRewriteUri(HttpContextcontext) { if(context.Request.Path.StartsWithSegments(_prefix))//判断访问是否含有前缀 { varnewUri=context.Request.Path.Value.Remove(0,_prefix.Value.Length)+context.Request.QueryString; vartargetUri=newUri(_newHost+newUri); returnTask.FromResult(targetUri); } returnTask.FromResult((Uri)null); } }
二.创建代理转发需要的ProxyHttpClient
创建独立的ProxyHttpClient,主要是为了区分代理转发的httpClient,方便后期添加日志或做别的处理.代码如下:
publicclassProxyHttpClient { publicHttpClientClient{get;privateset;} publicProxyHttpClient(HttpClienthttpClient) { Client=httpClient; } }
三.创建代理转发的中间件
代码如下,中间件嘛,主要就是Invoke方法了,说明可以看注释.
publicclassProxyMiddleware { //privateProxyHttpClient_proxyHttpClient; privateconststringCDN_HEADER_NAME="Cache-Control"; privatestaticreadonlystring[]NotForwardedHttpHeaders=new[]{"Connection","Host"}; privatereadonlyRequestDelegate_next; privatereadonlyILogger_logger; publicProxyMiddleware( RequestDelegatenext, ILogger logger ) { _next=next; _logger=logger; //_proxyHttpClient=proxyHttpClient; } /// ///通过中间件,拦截访问,检测前缀,并转发 /// ////// /// publicasyncTaskInvoke(HttpContextcontext,IUrlRewriterurlRewriter,ProxyHttpClientproxyHttpClient) { vartargetUri=awaiturlRewriter.RewriteUri(context); if(targetUri!=null) { varrequestMessage=GenerateProxifiedRequest(context,targetUri); awaitSendAsync(context,requestMessage,proxyHttpClient); return; } await_next(context); } privateasyncTaskSendAsync(HttpContextcontext,HttpRequestMessagerequestMessage,ProxyHttpClientproxyHttpClient) { using(varresponseMessage=awaitproxyHttpClient.Client.SendAsync(requestMessage,HttpCompletionOption.ResponseHeadersRead,context.RequestAborted)) { context.Response.StatusCode=(int)responseMessage.StatusCode; foreach(varheaderinresponseMessage.Headers) { context.Response.Headers[header.Key]=header.Value.ToArray(); } foreach(varheaderinresponseMessage.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"); } awaitresponseMessage.Content.CopyToAsync(context.Response.Body); } } privatestaticHttpRequestMessageGenerateProxifiedRequest(HttpContextcontext,UritargetUri) { varrequestMessage=newHttpRequestMessage(); CopyRequestContentAndHeaders(context,requestMessage); requestMessage.RequestUri=targetUri; requestMessage.Headers.Host=targetUri.Host; requestMessage.Method=GetMethod(context.Request.Method); returnrequestMessage; } privatestaticvoidCopyRequestContentAndHeaders(HttpContextcontext,HttpRequestMessagerequestMessage) { varrequestMethod=context.Request.Method; if(!HttpMethods.IsGet(requestMethod)&& !HttpMethods.IsHead(requestMethod)&& !HttpMethods.IsDelete(requestMethod)&& !HttpMethods.IsTrace(requestMethod)) { varstreamContent=newStreamContent(context.Request.Body); requestMessage.Content=streamContent; } foreach(varheaderincontext.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 { stringuserAgent=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); } } } } } privatestaticHttpMethodGetMethod(stringmethod) { if(HttpMethods.IsDelete(method))returnHttpMethod.Delete; if(HttpMethods.IsGet(method))returnHttpMethod.Get; if(HttpMethods.IsHead(method))returnHttpMethod.Head; if(HttpMethods.IsOptions(method))returnHttpMethod.Options; if(HttpMethods.IsPost(method))returnHttpMethod.Post; if(HttpMethods.IsPut(method))returnHttpMethod.Put; if(HttpMethods.IsTrace(method))returnHttpMethod.Trace; returnnewHttpMethod(method); }
四.注入和启用我们的中间件和ProxyHttpClient
我们在Startup的ConfigureServices中添加如下代码,注入我们的HttpClient与IUrlRewriter,如下:
services.AddHttpClient() .ConfigurePrimaryHttpMessageHandler(x=>newHttpClientHandler() { AllowAutoRedirect=false, MaxConnectionsPerServer=int.MaxValue, UseCookies=false, });//注入我们定义的HttpClient services.AddSingleton (newPrefixRewriter("/webapp","http://localhost:63445"));//这里填写前缀与需要转发的地址
然后在Startup的Configure中,启动我们的中间件,如下:
app.UseMiddleware();
五.测试中间件效果
我们编写前端代码如下:
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] publicActionResult>Get() { returnnewstring[]{"value1",accstring.ToString()}; } [HttpPost] publicAjaxResultPost(dynamicvalue) { stringaaa=JsonConvert.SerializeObject(value); returnSuccess("OK"); }
效果如下,可以看到我们的视图正确的获取到了返回值:
写在最后
这里我们通过中间件的形式实现了接口的代理转发,在具体的使用过程中肯定还会有一些小问题,而且这里我们只实现了Http的转发.ws的则没有.
如果要使用的话,其实国外有一个开源的项目:https://github.com/ProxyKit,已经有900多个star了.应该还不错.
到此这篇关于ASP.NETCore奇技淫巧之接口代理转发的实现的文章就介绍到这了,更多相关ASP.NETCore接口代理转发内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。