
简单,可扩展,快速,异步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。