
Server Web Async yang sederhana, dapat diskalakan, cepat, untuk memproses permintaan HTTP/HTTPS RESTF, yang ditulis dalam C#.
| Kemasan | Versi Nuget | Unduhan |
|---|---|---|
| Watson | ||
| Watson.lite | ||
| Watson.core |
Terima kasih khusus kepada @Damiendennehy karena telah mengizinkan kami menggunakan nama paket Watson.Core di Nuget!
Proyek ini adalah bagian dari .NET Foundation bersama dengan proyek lain seperti .NET Runtime.
SendChunk sekarang menerima isFinal sebagai properti BooleanTest.ServerSentEvents ProjectSaya ingin menyampaikan terima kasih khusus kepada mereka yang telah membantu membuat server web Watson lebih baik.
Watson adalah server web yang beroperasi di atas http.sys yang mendasarinya dalam sistem operasi. Watson.lite dibuat dengan menggabungkan httpserverlite. Watson.lite tidak memiliki ketergantungan pada http.sys , dan diimplementasikan menggunakan implementasi TCP yang disediakan oleh CavemantCP.
Ketergantungan pada http.sys (atau ketiadaannya) menciptakan perbedaan halus antara kedua perpustakaan, namun, konfigurasi dan manajemen masing -masing harus konsisten.
Watson.lite umumnya kurang kinerja daripada Watson, karena implementasi HTTP berada di ruang pengguna.
127.0.0.1 atau localhostX509Certificate2 harus disediakan Watson dan Watson.lite selalu merutekan dalam urutan berikut (konfigurasikan menggunakan Webserver.Routes ):
.Preflight - menangani permintaan preflight (umumnya dengan OPTIONS metode http).PreRouting - selalu dipanggil sebelum penentuan perutean dibuat.PreAuthentication - Grup routing, terdiri dari:.Static - rute statis, misalnya metode HTTP dan URL eksplisit.Content - file rute yang melayani, misalnya direktori di mana file dapat dibaca.Parameter - rute di mana variabel ditentukan dalam jalur, misalnya /user/{id}.Dynamic - rute di mana URL ditentukan oleh ekspresi reguler.AuthenticateRequest - rute demarkasi antara rute yang tidak otentikasi dan diautentikasi.PostAuthentication - kelompok perutean dengan struktur yang identik dengan .PreAuthentication.Default - rute default; Semua permintaan pergi ke sini jika tidak dialihkan sebelumnya.PostRouting - selalu dipanggil, umumnya untuk penebangan dan telemetri Jika Anda tidak ingin menggunakan otentikasi, Anda harus memetakan rute Anda di grup routing .PreAuthentication (meskipun secara teknis mereka dapat ditempatkan di .PostAuthentication atau .Default dengan asumsi metode AuthenticateRequest adalah nol.
Sebagai aturan umum, jangan pernah mencoba mengirim data ke HttpResponse saat berada di rute .PostRouting . Jika respons telah dikirim, upaya di dalam .PostRouting akan gagal.
Disarankan agar Anda menerapkan otentikasi di .AuthenticateRequest . Jika permintaan gagal otentikasi, mengembalikan respons dalam rute itu. Kelas HttpContextBase memiliki properti yang dapat menahan metadata terkait otentikasi atau sesi, khususnya, .Metadata .
Secara default, Watson dan Watson.lite akan mengizinkan semua koneksi masuk.
Server.AccessControl.DenyList.Add(ip, netmask)Server.AccessControl.Mode = AccessControlMode.DefaultDenyServer.AccessControl.PermitList.Add(ip, netmask) Lihat Test.Default untuk contoh lengkap.
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!" ) ; Kemudian, buka browser Anda ke http://127.0.0.1:9000/ .
Lihat Test.Routing untuk contoh lengkap.
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 mendukung baik menerima data yang dipotong dan mengirim data yang dipotong (ditunjukkan oleh header Transfer-Encoding: chunked ).
Lihat Test.ChunkServer untuk implementasi sampel.
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 ;
} Watson mendukung pengiriman acara server-sent. Lihat Test.ServerSentEvents untuk implementasi sampel. Metode SendEvent menangani data: dan nn berikut ke pesan Anda.
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 membantu Anda mengatur server Anda lebih mudah dengan memperkenalkan rantai pengaturan dan rute daripada menggunakan kelas server secara langsung. Terima kasih khusus kepada @SapUrtComputer30 untuk memproduksi fitur bagus ini!
Lihat Test.HostBuilder untuk implementasi sampel lengkap.
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!" ) ; Ketika Anda mengonfigurasi Watson untuk mendengarkan pada 127.0.0.1 atau localhost , itu hanya akan menanggapi permintaan yang diterima dari dalam mesin lokal.
Untuk mengonfigurasi akses dari node lain di luar localhost , gunakan yang berikut:
* atau + . Anda harus menjalankan sebagai administrator (batasan sistem operasi)netsh :netsh http show urlaclnetsh http add urlacl url=http://[hostname]:[port]/ user=everyone listen=yeshostname dan port adalah nilai yang Anda gunakan di konstruktor Ketika Anda mengonfigurasi watson.lite untuk mendengarkan pada 127.0.0.1 , itu hanya akan menanggapi permintaan yang diterima dari dalam mesin lokal.
Untuk mengonfigurasi akses dari node lain di luar mesin lokal, gunakan yang berikut:
* atau + . Anda harus menjalankan sebagai administrator (batasan sistem operasi)X509Certificate2 Silakan merujuk ke proyek Test.Docker dan file Docker.md di dalamnya.
Lihat Changelog.md untuk riwayat versi.