
เว็บเซิร์ฟเวอร์ที่เรียบง่ายปรับขนาดได้อย่างรวดเร็วสำหรับการประมวลผลคำขอ HTTP/HTTPS RESTFUL ซึ่งเขียนด้วย C#
| บรรจุุภัณฑ์ | เวอร์ชัน NuGet | การดาวน์โหลด |
|---|---|---|
| วัตสัน | ||
| Watson.lite | ||
| Watson.Core |
ขอขอบคุณเป็นพิเศษกับ @damiendennehy ที่อนุญาตให้เราใช้ชื่อแพ็คเกจ Watson.Core ใน Nuget!
โครงการนี้เป็นส่วนหนึ่งของมูลนิธิ. NET พร้อมกับโครงการอื่น ๆ เช่น. NET Runtime
SendChunk ตอนนี้ยอมรับ isFinal เป็นคุณสมบัติ BooleanTest.ServerSentEvents Projectฉันขอขอบคุณเป็นพิเศษสำหรับผู้ที่ช่วยให้ Watson Webserver ดีขึ้น
วัตสันเป็นเว็บเซิร์ฟเวอร์ที่ทำงานอยู่ด้านบนของ http.sys พื้นฐานภายในระบบปฏิบัติการ Watson.lite ถูกสร้างขึ้นโดยการรวม httpserverlite Watson.lite ไม่มีการพึ่งพา http.sys และดำเนินการโดยใช้การใช้งาน TCP ที่จัดทำโดย CAVEMANTCP
การพึ่งพา http.sys (หรือขาดมัน) สร้างความแตกต่างเล็กน้อยระหว่างสองห้องสมุดอย่างไรก็ตามการกำหนดค่าและการจัดการของแต่ละคนควรสอดคล้องกัน
Watson.lite โดยทั่วไปมีประสิทธิภาพน้อยกว่า Watson เนื่องจากการใช้งาน HTTP อยู่ในพื้นที่ผู้ใช้
127.0.0.1 หรือ localhostX509Certificate2 Watson และ Watson.lite จะกำหนดเส้นทางตามลำดับต่อไปนี้ (กำหนดค่าโดยใช้ Webserver.Routes ):
.Preflight - การจัดการคำขอ preflight (โดยทั่วไปมี OPTIONS วิธี HTTP).PreRouting - เรียกใช้เสมอก่อนที่จะมีการกำหนดเส้นทางใด ๆ.PreAuthentication - กลุ่มเส้นทางประกอบด้วย:.Static - เส้นทางคงที่เช่นวิธี HTTP และ URL ที่ชัดเจน.Content - เส้นทางการให้บริการไฟล์เช่นไดเรกทอรีที่สามารถอ่านไฟล์ได้.Parameter - เส้นทางที่ระบุตัวแปรในพา ธ เช่น /user/{id}.Dynamic - เส้นทางที่ URL ถูกกำหนดโดยนิพจน์ทั่วไป.AuthenticateRequest - เส้นทางการแบ่งเขตระหว่างเส้นทางที่ไม่ผ่านการตรวจสอบและการรับรองความถูกต้อง.PostAuthentication - กลุ่มเส้นทางที่มีโครงสร้างเหมือนกับ .PreAuthentication.Default - เส้นทางเริ่มต้น; คำขอทั้งหมดไปที่นี่หากไม่ได้กำหนดเส้นทางก่อนหน้านี้.PostRouting - เรียกใช้เสมอโดยทั่วไปสำหรับการบันทึกและ telemetry หากคุณไม่ต้องการใช้การรับรองความถูกต้องคุณควรแม AuthenticateRequest .Default .PostAuthentication คุณในกลุ่มการกำหนด .PreAuthentication
ตามกฎทั่วไปอย่าพยายามส่งข้อมูลไปยัง HttpResponse ในขณะที่อยู่ใน .PostRouting . หากการตอบกลับถูกส่งไปแล้วความพยายามภายใน .PostRouting จะล้มเหลว
ขอแนะนำให้คุณใช้การรับรองความถูกต้องใน .AuthenticateRequest หากคำขอล้มเหลวการตรวจสอบความถูกต้องให้ส่งคืนการตอบกลับภายในเส้นทางนั้น คลาส HttpContextBase มีคุณสมบัติที่สามารถเก็บข้อมูลเมตาดาต้าที่เกี่ยวข้องกับการตรวจสอบความถูกต้องหรือที่เกี่ยวข้องกับเซสชันโดยเฉพาะ . .Metadata
โดยค่าเริ่มต้น Watson และ Watson.lite จะอนุญาตให้มีการเชื่อมต่อขาเข้าทั้งหมด
Server.AccessControl.DenyList.Add(ip, netmask)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" ) ; วัตสันรองรับทั้งข้อมูลที่ได้รับและส่งข้อมูล chunked (ระบุโดย 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!" ) ; เมื่อคุณกำหนดค่าวัตสันให้ฟังบน 127.0.0.1 หรือ localhost มันจะตอบสนองต่อคำขอที่ได้รับจากภายในเครื่องเท่านั้น
ในการกำหนดค่าการเข้าถึงจากโหนดอื่น ๆ นอก localhost ให้ใช้สิ่งต่อไปนี้:
* หรือ + คุณต้องทำงานเป็นผู้ดูแลระบบ (ข้อ จำกัด ของระบบปฏิบัติการ)netsh :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 สำหรับประวัติเวอร์ชัน