
Простой, масштабируемый, быстрый, асинхронный веб -сервер для обработки запросов RESTFUL HTTP/HTTPS, написанный в C#.
| Упаковка | NUGET версия | Загрузки |
|---|---|---|
| Уотсон | ||
| Watson.Lite | ||
| Watson.core |
Особая спасибо @DamiendenNehy за то, что он позволил нам использовать название пакета Watson.Core в Nuget!
Этот проект является частью Фонда .NET наряду с другими проектами, такими как время выполнения .NET.
SendChunk теперь принимает isFinal в качестве Boolean имуществаTest.ServerSentEventsЯ хотел бы выразить особую благодарность тем, кто помог сделать 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 - обработка запросов на предварительную работу (обычно с OPTIONS метода HTTP).PreRouting - Всегда призван перед каким -либо определением маршрутизации.PreAuthentication - группа маршрутизации, состоящая из:.Static - статические маршруты, например, метод HTTP и явный URL.Content - файловые маршруты обслуживания, например, каталог, где файлы можно прочитать.Parameter - Маршруты, где переменные указаны в пути, например /user/{id}.Dynamic - маршруты, где URL определяется регулярным выражением.AuthenticateRequest - Разграничение маршрута между несаутентированными и аутентифицированными маршрутами.PostAuthentication - группа маршрутизации со структурой, идентичной .PreAuthentication.Default - маршрут по умолчанию; Все запросы идут сюда, если не маршрутируются ранее.PostRouting - всегда вызывается, как правило, для регистрации и телеметрии Если вы не хотите использовать аутентификацию, вы должны сопоставить свои маршруты в группе маршрутизации .PreAuthentication (хотя технически их можно помещать в .PostAuthentication или .Default , предполагая, что метод AuthenticateRequest является нулевым.
Как правило, никогда не пытайтесь отправлять данные в 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" ) ; Уотсон поддерживает как полученные данные, так и отправляя данные о чистоте (обозначенные 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 :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 для истории версий.