.Net反向代理組件Yarp用法詳解
簡(jiǎn)介
Yarp 是微軟團(tuán)隊(duì)開(kāi)發(fā)的一個(gè)反向代理組件, 除了常規(guī)的 http 和 https 轉(zhuǎn)換通訊,它最大的特點(diǎn)是可定制化,很容易根據(jù)特定場(chǎng)景開(kāi)發(fā)出需要的定制代理通道。
詳細(xì)介紹:https://devblogs.microsoft.com/dotnet/announcing-yarp-1-0-release
源碼倉(cāng)庫(kù):https://github.com/microsoft/reverse-proxy
文檔地址 :https://microsoft.github.io/reverse-proxy/
基礎(chǔ)使用
1、創(chuàng)建 ASP.NET Core 空項(xiàng)目
使用 Visual Studio :

使用 .NET CLI 命令行創(chuàng)建:
dotnet new web -o MyProxy
2、 修改代碼 Program.cs 文件
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapGet("/Ping", () => "Hello World!");
app.MapReverseProxy();
app.Run();3、修改配置文件 appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ReverseProxy": {
"Routes": {
"routeAll": {
"ClusterId": "clusterBaidu",
"Match": {
"Path": "{**catch-all}"
}
}
},
"Clusters": {
"clusterBaidu": {
"Destinations": {
"baidu": {
"Address": "https://www.baidu.com/"
}
}
}
}
}
}這里的配置是將所有的請(qǐng)求都轉(zhuǎn)發(fā)到百度。
在 Program.cs 里,還注冊(cè)了一個(gè) Get 路由 Ping 。
4、啟動(dòng)項(xiàng)目

能夠看到在瀏覽器訪(fǎng)問(wèn)程序監(jiān)聽(tīng)的端口號(hào)后,顯示的是百度的頁(yè)面。打開(kāi) F12 ,看到請(qǐng)求頭也是本地的,并不是百度的域名。
測(cè)試手動(dòng)注冊(cè)的路由 Ping :

能夠顯示正常。
5、問(wèn)題整理
- (1) Yarp 是不是只能做這種簡(jiǎn)單的轉(zhuǎn)發(fā)?
不是,往下有配置文件說(shuō)明。
- (2) JSON 配置文件里有什么要注意的地方嗎?
有。在這個(gè)演示的配置文件中 ReverseProxy:Clusters:cluster1:Destinations:destination1:Address 對(duì)應(yīng)的值是:https://www.baidu.com/ ,如果去掉 www ,在項(xiàng)目啟動(dòng)后會(huì)跳轉(zhuǎn)到百度首頁(yè),不是代理轉(zhuǎn)發(fā)。去掉末尾的 / 符合沒(méi)有任何影響。
- (3) Yarp 會(huì)影響到程序中注冊(cè)的路由嗎?
不會(huì)影響到程序內(nèi)部注冊(cè)的路由。在 Program.cs 中無(wú)論 app.MapReverseProxy(); 在上還是在下,在訪(fǎng)問(wèn) Ping 的時(shí)候,都是返回 Hello World!
var app = builder.Build();
app.MapReverseProxy();
app.MapGet("/Ping", () => "Hello World!");
app.Run();進(jìn)階探索
1、多地址代理
修改配置文件 appsettings.json ,實(shí)現(xiàn)默認(rèn)路由跳轉(zhuǎn)百度,當(dāng)訪(fǎng)問(wèn) /movie 是訪(fǎng)問(wèn) b站。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ReverseProxy": {
"Routes": {
"routeBaidu": {
"ClusterId": "clusterBaidu",
"Match": {
"Path": "{**catch-all}"
}
},
"routeBiliBili": {
"ClusterId": "clusterBiliBili",
"Match": {
"Path": "/movie/{**catch-all}"
}
}
},
"Clusters": {
"clusterBaidu": {
"Destinations": {
"baidu": {
"Address": "https://www.baidu.com/"
}
}
},
"clusterBiliBili": {
"Destinations": {
"bilibili": {
"Address": "https://www.bilibili.com/"
}
}
}
}
}
}測(cè)試結(jié)果:


在后面輸入路由 /movie 后能夠跳轉(zhuǎn)到b站。但是b站網(wǎng)頁(yè)沒(méi)有完整顯示,圖片都沒(méi)有,這是網(wǎng)站上的策略問(wèn)題,對(duì)于數(shù)據(jù)接口沒(méi)有這些問(wèn)題。

詳細(xì)的配置文件說(shuō)明,可以查看 https://microsoft.github.io/reverse-proxy/articles/config-files.html
2、規(guī)則匹配
網(wǎng)頁(yè)上太多資源,為了方便測(cè)試,啟用兩個(gè) api 接口。地址分別是:http://localhost:5241/ 和 https://localhost:7184/
兩個(gè) api 接口中分別注冊(cè) /test 路由。
// http://localhost:5241/
app.MapGet("/test", () => "Welcome to Api111!");
// https://localhost:7184/
app.MapGet("/test", () => "Welcome to Api222!");啟動(dòng)兩個(gè) api 程序。
C:\Users\Test>curl http://localhost:5241/test Welcome to Api111! C:\Users\Test>curl https://localhost:7184/test Welcome to Api222!
修改 MyProxy 項(xiàng)目的配置文件 appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ReverseProxy": {
"Routes": {
"routeOne": {
"ClusterId": "clusterOne",
"Match": {
"Path": "/test/{**catch-all}",
"QueryParameters": [
{
"Name": "number",
"Values": [ "1" ]
}
]
}
},
"routeTwo": {
"ClusterId": "clusterTwo",
"Match": {
"Path": "/test/{**catch-all}",
"QueryParameters": [
{
"Name": "number",
"Values": [ "2" ]
}
]
}
},
"routeBaidu": {
"ClusterId": "clusterBaidu",
"Match": {
"Path": "{**catch-all}"
}
}
},
"Clusters": {
"clusterOne": {
"Destinations": {
"apiOne": {
"Address": "http://localhost:5241/"
}
}
},
"clusterTwo": {
"Destinations": {
"apiTwo": {
"Address": "https://localhost:7184/"
}
}
},
"clusterBaidu": {
"Destinations": {
"baidu": {
"Address": "https://www.baidu.com/"
}
}
}
}
}
}- Path :監(jiān)聽(tīng)路由地址。
- QueryParameters:匹配參數(shù)。
- QueryParameters:Name:參數(shù)名。
- QueryParameters:Values:參數(shù)值。
MyProxy 的監(jiān)聽(tīng)端口是 http://localhost:5024/ 訪(fǎng)問(wèn)結(jié)果如下:
C:\Users\Test>curl http://localhost:5024/ping Hello World! C:\Users\Test>curl http://localhost:5024/test <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> </head><body> <h1>Not Found</h1> <p>The requested URL /test was not found on this server.</p> </body></html> C:\Users\Test>curl http://localhost:5024/test?number=1 Welcome to Api111! C:\Users\Test>curl http://localhost:5024/test?number=2 Welcome to Api222!
能夠根據(jù)參數(shù)以及參數(shù)值導(dǎo)向?qū)?yīng)的地址。
3、問(wèn)題整理
- (1)為什么訪(fǎng)問(wèn) /movie 不能正常顯示網(wǎng)頁(yè)。
因?yàn)?b站某些接口開(kāi)啟了防盜鏈,還有跨域檢測(cè)。


- (2)在根據(jù)參數(shù)匹配中,如果匹配的路由一樣,監(jiān)聽(tīng)的參數(shù)一樣,參數(shù)值也一樣會(huì)怎么樣?
訪(fǎng)問(wèn)該路由地址會(huì)報(bào)錯(cuò)。
- (3)路由匹配的優(yōu)先級(jí)?
程序內(nèi)注冊(cè)的路由優(yōu)先級(jí)最高,其次才是 Yarp 在配置文件里加載的。
小試牛刀
最近的工作是做企業(yè)內(nèi)數(shù)據(jù)安全方面的。推動(dòng)公司數(shù)據(jù)安全體系,通過(guò)技術(shù)手段提升公司信息安全。
有一個(gè)很老的OA系統(tǒng),十幾年了, .NET Framework 2.0 寫(xiě)的。漏洞一大堆,包括不限于xss、sql注入等,權(quán)限只到表單級(jí)別。瀏覽器上按下 F12 能查看到表單鏈接,直接復(fù)制出去,別人也能訪(fǎng)問(wèn)。
在這個(gè)系統(tǒng)上要做安全,我想的是在中間加代理,正好適合使用 Yarp 來(lái)完成,也方便寫(xiě)業(yè)務(wù)處理代碼。嗯,很真實(shí), .NET Core 寫(xiě)的,方便寫(xiě)業(yè)務(wù)代碼。
用戶(hù)登錄成功后,會(huì)記錄下用戶(hù)的 Host 和 Cookie,每次訪(fǎng)問(wèn)的時(shí)候系統(tǒng)的時(shí)候,在 Yarp 這里都校驗(yàn)一下是否與用戶(hù)登錄時(shí)的匹配。
解決了兩個(gè)問(wèn)題:
1、從網(wǎng)絡(luò)層捕獲到所有的請(qǐng)求,方便后面做排查。參數(shù)、傳值,出了事故可以找到責(zé)任人。
2、隔離真實(shí)的站點(diǎn)地址,杜絕弱安全等級(jí)網(wǎng)站暴露后被壞人攻擊的風(fēng)險(xiǎn)。

踩坑集錦
1、non-ASCII
項(xiàng)目要代理某網(wǎng)頁(yè),在使用下載功能的時(shí)候,接口返回 502 。
info: Yarp.ReverseProxy.Forwarder.HttpForwarder[48]
ResponseHeaders: The destination returned a response that cannot be proxied back to the client.
System.InvalidOperationException: Invalid non-ASCII or control character in header: 0x00E4
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowInvalidHeaderCharacter(Char ch)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ValidateHeaderValueCharacters(StringValues headerValues)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseHeaders.SetValueFast(String key, StringValues value)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value)
at Yarp.ReverseProxy.Forwarder.HttpTransformer.CopyResponseHeaders(HttpHeaders source, IHeaderDictionary destination)
at Yarp.ReverseProxy.Forwarder.HttpTransformer.TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse)
at Yarp.ReverseProxy.Transforms.Builder.StructuredTransformer.TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse)
at Yarp.ReverseProxy.Forwarder.HttpForwarder.SendAsync(HttpContext context, String destinationPrefix, HttpMessageInvoker httpClient, ForwarderRequestConfig requestConfig, HttpTransformer transformer)去 GitHub 翻 Issues

下載接口能正常訪(fǎng)問(wèn),文件流也能完整地拿到。重寫(xiě)了所有的響應(yīng)頭沒(méi)有用。這種不開(kāi)源的商業(yè)站點(diǎn),也猜不到字符編碼。
最后妥協(xié)了,用了一個(gè) .NET 服務(wù)在服務(wù)器上下載后再轉(zhuǎn)發(fā)。
代理非常規(guī)服務(wù)接口時(shí),一定要多測(cè)試。
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接
相關(guān)文章
ASP.NET MVC中HtmlHelper控件7個(gè)大類(lèi)中各個(gè)控件使用詳解
本文主要介紹HtmlHelper類(lèi)控件的使用方法,給初涉MVC的朋友一些幫助,有需要的朋友可以參考一下。2016-03-03
asp.net 無(wú)刷新翻頁(yè)就是這么簡(jiǎn)單
前兩天看了一個(gè)自定義分頁(yè)控件,和AspNetPager一樣是實(shí)現(xiàn)IPostBackEventHandler接口,不過(guò)簡(jiǎn)潔許多,就想能不能實(shí)現(xiàn)ICallbackEventHandler接口做到無(wú)刷新分頁(yè)呢?想到了就馬上去做,終于,設(shè)想變成了現(xiàn)實(shí)!!2010-03-03
Linq to XML 用一句話(huà)讀出RSS文章列表代碼
Linq to XML 用一句話(huà)讀出RSS文章列表代碼2010-06-06
asp.net實(shí)現(xiàn)文件無(wú)刷新上傳方法匯總
本文給大家介紹的是asp.net實(shí)現(xiàn)文件無(wú)刷新上傳的2種方法,分別是使用swfupload插件和uploadify插件,講述的十分細(xì)致全面,附上示例,有需要的小伙伴可以參考下。2015-06-06
asp.net導(dǎo)出Excel類(lèi)庫(kù)代碼分享
這篇文章主要介紹了asp.net導(dǎo)出Excel類(lèi)庫(kù)代碼,有需要的朋友可以參考一下2013-12-12
ASP.NET百度Ueditor編輯器實(shí)現(xiàn)上傳圖片添加水印效果
這篇文章主要給大家介紹了ASP.NET百度Ueditor編輯器1.4.3這個(gè)版本實(shí)現(xiàn)上傳圖片添加水印效果的相關(guān)資料,文中通過(guò)圖文及示例代碼介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-03-03
ASP.NET Core Web資源打包與壓縮技術(shù)介紹
這篇文章介紹了ASP.NET Core Web資源打包與壓縮技術(shù),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-02-02

