
簡單,可擴展,快速,異步Web服務器,用於處理用C#編寫的RESTFUL HTTP/HTTPS請求。
| 包裹 | Nuget版本 | 下載 |
|---|---|---|
| 沃森 | ||
| watson.lite | ||
| 沃森 |
特別感謝@damiendennehy允許我們在Nuget中使用Watson.Core 。
該項目是.NET基金會的一部分,以及其他項目,例如.NET運行時。
SendChunk現在接受isFinal作為Boolean物業Test.ServerSentEvents項目我要特別感謝那些幫助Watson Web服務器更好的人。
沃森(Watson)是一個網絡服務器,在操作系統內的基礎http.sys上運行。 watson.lite是通過合併httpserverlite創建的。 watson.lite不依賴於http.sys ,並且使用CavemantCP提供的TCP實現實施。
對http.sys (或缺乏)的依賴性在兩個庫之間產生細微的差異,但是,每個庫的配置和管理應保持一致。
Watson.lite通常比Watson的性能要低,因為HTTP實現在用戶空間中。
127.0.0.1或localhost則可能需要高程(管理特權)X509Certificate2 watson和watson.lite始終按以下順序路由(使用Webserver.Routes配置):
.Preflight OPTIONS.PreRouting.PreAuthentication - 一個路由組,由:.Static - 靜態路線,例如HTTP方法和顯式URL.Content文件服務路線,例如可以讀取文件的目錄.Parameter - 路徑在路徑中指定變量的路由,例如/user/{id}.Dynamic.AuthenticateRequest.PostAuthentication .PreAuthentication.Default默認路由;如果以前沒有路由,所有請求都可以轉到此處.PostRouting如果您不想使用身份驗證,則應將路由映射在.PreAuthentication路由組中(儘管從技術上講可以將它們放入.PostAuthentication或.Default假設AuthenticateRequest方法是無效的。
通常,在.PostRouting路線中,切勿嘗試將數據發送到HttpResponse 。如果已經發送響應,則.PostRouting內部的嘗試將失敗。
建議您在.AuthenticateRequest中實現身份驗證。如果請求失敗身份驗證,請在該路線內返迴響應。 HttpContextBase類具有可以容納與身份驗證相關或與會話相關的元數據的屬性,特別是.Metadata 。
默認情況下,Watson和Watson.lite將允許所有入站連接。
Server.AccessControl.DenyList.Add(ip, netmask) IPS或網絡Server.AccessControl.Mode = AccessControlMode.DefaultDenyServer.AccessControl.PermitList.Add(ip, netmask)有關完整示例,請參閱Test.Default 。
using System . IO ;
using System . Text ;
using WatsonWebserver ;
static void Main ( string [ ] args )
{
WebserverSettings settings = new WebserverSettings ( "127.0.0.1" , 9000 ) ;
Webserver server = new Webserver ( settings , DefaultRoute ) ;
server . Start ( ) ;
Console . ReadLine ( ) ;
}
static async Task DefaultRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the default route!" ) ;然後,將瀏覽器打開至http://127.0.0.1:9000/ 。
有關完整示例,請參閱Test.Routing 。
using System . IO ;
using System . Text ;
using System . Text . RegularExpressions ;
using WatsonWebserver ;
static void Main ( string [ ] args )
{
WebserverSettings settings = new WebserverSettings ( "127.0.0.1" , 9000 ) ;
Webserver server = new Webserver ( settings , DefaultRoute ) ;
// add content routes
server . Routes . PreAuthentication . Content . Add ( "/html/" , true ) ;
server . Routes . PreAuthentication . Content . Add ( "/img/watson.jpg" , false ) ;
// add static routes
server . Routes . PreAuthentication . Static . Add ( HttpMethod . GET , "/hello/" , GetHelloRoute ) ;
server . Routes . PreAuthentication . Static . Add ( HttpMethod . GET , "/howdy/" , async ( HttpContextBase ctx ) =>
{
await ctx . Response . Send ( "Hello from the GET /howdy static route!" ) ;
return ;
} ) ;
// add parameter routes
server . Routes . PreAuthentication . Parameter . Add ( HttpMethod . GET , "/{version}/bar" , GetBarRoute ) ;
// add dynamic routes
server . Routes . PreAuthentication . Dynamic . Add ( HttpMethod . GET , new Regex ( "^/foo/ \ d+$" ) , GetFooWithId ) ;
server . Routes . PreAuthentication . Dynamic . Add ( HttpMethod . GET , new Regex ( "^/foo/?$" ) , GetFoo ) ;
// start the server
server . Start ( ) ;
Console . WriteLine ( "Press ENTER to exit" ) ;
Console . ReadLine ( ) ;
}
static async Task GetHelloRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the GET /hello static route!" ) ;
static async Task GetBarRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the GET /" + ctx . Request . Url . Parameters [ "version" ] + "/bar route!" ) ;
static async Task GetFooWithId ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the GET /foo/[id] dynamic route!" ) ;
static async Task GetFoo ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the GET /foo/ dynamic route!" ) ;
static async Task DefaultRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the default route!" ) ; server . Routes . PreAuthentication . Static . Add ( HttpMethod . GET , "/hello/" , GetHelloRoute , MyExceptionRoute ) ;
static async Task GetHelloRoute ( HttpContextBase ctx ) => throw new Exception ( "Whoops!" ) ;
static async Task MyExceptionRoute ( HttpContextBase ctx , Exception e )
{
ctx . Response . StatusCode = 500 ;
await ctx . Response . Send ( e . Message ) ;
} Webserver server = new Webserver ( "127.0.0.1" , 9000 , false , DefaultRoute ) ;
// set default permit (permit any) with deny list to block specific IP addresses or networks
server . Settings . AccessControl . Mode = AccessControlMode . DefaultPermit ;
server . Settings . AccessControl . DenyList . Add ( "127.0.0.1" , "255.255.255.255" ) ;
// set default deny (deny all) with permit list to permit specific IP addresses or networks
server . Settings . AccessControl . Mode = AccessControlMode . DefaultDeny ;
server . Settings . AccessControl . PermitList . Add ( "127.0.0.1" , "255.255.255.255" ) ; 沃森(Watson)支持接收塊的數據和發送塊數據(由標題Transfer-Encoding: chunked表示)。
有關示例實現,請參閱Test.ChunkServer 。
static async Task UploadData ( HttpContextBase ctx )
{
if ( ctx . Request . ChunkedTransfer )
{
bool finalChunk = false ;
while ( ! finalChunk )
{
Chunk chunk = await ctx . Request . ReadChunk ( ) ;
// work with chunk.Length and chunk.Data (byte[])
finalChunk = chunk . IsFinalChunk ;
}
}
else
{
// read from ctx.Request.Data stream
}
} static async Task DownloadChunkedFile ( HttpContextBase ctx )
{
using ( FileStream fs = new FileStream ( "./img/watson.jpg" , , FileMode . Open , FileAccess . Read ) )
{
ctx . Response . StatusCode = 200 ;
ctx . Response . ChunkedTransfer = true ;
byte [ ] buffer = new byte [ 4096 ] ;
while ( true )
{
int bytesRead = await fs . ReadAsync ( buffer , 0 , buffer . Length ) ;
byte [ ] data = new byte [ bytesRead ] ;
Buffer . BlockCopy ( buffer , 0 , bytesRead , data , 0 ) ; // only copy the read data
if ( bytesRead > 0 )
{
await ctx . Response . SendChunk ( data , false ) ;
}
else
{
await ctx . Response . SendChunk ( Array . Empty < byte > ( ) , true ) ;
break ;
}
}
}
return ;
} 沃森支持發送服務器量事件。有關示例實現,請參閱Test.ServerSentEvents 。 SendEvent方法處理預先data:以及您的消息中的以下nn 。
static async Task SendEvents ( HttpContextBase ctx )
{
ctx . Response . StatusCode = 200 ;
ctx . Response . ServerSentEvents = true ;
while ( true )
{
string data = GetNextEvent ( ) ; // your implementation
if ( ! String . IsNullOrEmpty ( data ) )
{
await ctx . Response . SendEvent ( data ) ;
}
else
{
break ;
}
}
return ;
} HostBuilder通過引入設置和路由鏈而不是直接使用服務器類,可以幫助您更輕鬆地設置服務器。特別感謝 @sapurtcomputer30生產此精美功能!
有關完整的示例實現,請參閱Test.HostBuilder 。
using WatsonWebserver . Extensions . HostBuilderExtension ;
Webserver server = new HostBuilder ( "127.0.0.1" , 8000 , false , DefaultRoute )
. MapStaticRoute ( HttpMethod . GET , GetUrlsRoute , "/links" )
. MapStaticRoute ( HttpMethod . POST , CheckLoginRoute , "/login" )
. MapStaticRoute ( HttpMethod . POST , TestRoute , "/test" )
. Build ( ) ;
server . Start ( ) ;
Console . WriteLine ( "Server started" ) ;
Console . ReadKey ( ) ;
static async Task DefaultRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from default route!" ) ;
static async Task GetUrlsRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Here are your links!" ) ;
static async Task CheckLoginRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Checking your login!" ) ;
static async Task TestRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the test route!" ) ; 當您配置Watson在127.0.0.1或localhost上收聽時,它將僅響應從本地計算機中收到的請求。
要配置localhost以外其他節點的訪問,請使用以下內容:
*或+ 。您必須作為管理員(操作系統限制)運行netsh命令在操作系統中添加URL ACL:IE URL綁定:netsh http show urlaclnetsh http add urlacl url=http://[hostname]:[port]/ user=everyone listen=yeshostname和port是您在構造函數中使用的值當您配置watson.lite以在127.0.0.1上收聽時,它將僅響應從本地計算機中收到的請求。
要配置來自本地計算機外其他節點的訪問,請使用以下內容:
*或+ 。您必須作為管理員(操作系統限制)運行X509Certificate2對象請參閱其中的Test.Docker項目和其中的Docker.md文件。
有關版本歷史記錄,請參閱ChangElog.md。