
Servidor web simples, escalável, rápido, Async para processar solicitações RESTful HTTP/HTTPS, escritas em C#.
| Pacote | Versão Nuget | Downloads |
|---|---|---|
| Watson | ||
| Watson.lite | ||
| Watson.core |
Agradecimentos especiais a @damiendennehy por nos permitir o uso do nome do pacote Watson.Core em Nuget!
Este projeto faz parte da fundação .NET, juntamente com outros projetos, como o .NET Runtime.
SendChunk agora aceita isFinal como uma propriedade BooleanTest.ServerSentEventsGostaria de agradecer especial aos que ajudaram a melhorar o Watson Webs.
Watson é um servidor da web que opera no topo do http.sys subjacente no sistema operacional. Watson.lite foi criado mesclando httpServerlite. O Watson.lite não tem uma dependência de http.sys e é implementado usando uma implementação TCP fornecida pelo CaveMantcp.
A dependência de http.sys (ou falta dela) cria diferenças sutis entre as duas bibliotecas, no entanto, a configuração e o gerenciamento de cada um deve ser consistente.
O Watson.lite é geralmente menos executado que o Watson, porque a implementação HTTP está no espaço do usuário.
127.0.0.1 ou localhostX509Certificate2 , deve ser fornecido Watson e Watson.lite sempre rotas na seguinte ordem (configure usando Webserver.Routes ):
.Preflight - Lidando com solicitações de pré -voo (geralmente com OPTIONS de método HTTP).PreRouting - sempre invocado antes que qualquer determinação de roteamento seja feita.PreAuthentication - um grupo de roteamento, composto por:.Static - rotas estáticas, por exemplo, um método HTTP e um URL explícito.Content - Arquivos que servem rotas, por exemplo, um diretório onde os arquivos podem ser lidos.Parameter - rotas onde as variáveis são especificadas no caminho, por exemplo, /user/{id}.Dynamic - rotas onde o URL é definido por uma expressão regular.AuthenticateRequest - Rota de demarcação entre rotas não autenticadas e autenticadas.PostAuthentication - um grupo de roteamento com uma estrutura idêntica à .PreAuthentication.Default - a rota padrão; Todas as solicitações vão aqui se não for roteado anteriormente.PostRouting - sempre chamado, geralmente para extração e telemetria Se você não deseja usar a autenticação, mapeie suas rotas no grupo de roteamento .PreAuthentication (embora tecnicamente eles possam ser colocados em .PostAuthentication ou .Default assumindo que o método AuthenticateRequest é nulo.
Como regra geral, nunca tente enviar dados para uma HttpResponse enquanto estiver na rota .PostRouting . Se uma resposta já tiver sido enviada, a tentativa dentro do .PostRouting falhará.
Recomenda -se que você implemente a autenticação em .AuthenticateRequest . Se uma solicitação falha na autenticação, retorne uma resposta dentro dessa rota. A classe HttpContextBase possui propriedades que podem conter metadados relacionados à autenticação ou relacionados à sessão, especificamente, .Metadata .
Por padrão, Watson e Watson.lite permitirão todas as conexões de entrada.
Server.AccessControl.DenyList.Add(ip, netmask)Server.AccessControl.Mode = AccessControlMode.DefaultDenyServer.AccessControl.PermitList.Add(ip, netmask) Consulte Test.Default para obter um exemplo completo.
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!" ) ; Em seguida, abra seu navegador para http://127.0.0.1:9000/ .
Consulte Test.Routing para um exemplo completo.
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" ) ; O Watson suporta os dados que recebem dados e o envio de dados rastreados (indicado pelo Transfer-Encoding: chunked ).
Consulte Test.ChunkServer para obter uma implementação de amostra.
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 ;
} O Watson suporta o envio de eventos enviados pelo servidor. Consulte o Test.ServerSentEvents para uma implementação de amostra. O método SendEvent lida com data: e o seguinte nn na sua mensagem.
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 ajuda você a configurar seu servidor com muito mais facilidade, introduzindo uma cadeia de configurações e rotas em vez de usar a classe do servidor diretamente. Agradecimentos especiais a @SapurtComputer30 por produzir esse recurso fino!
Consulte Test.HostBuilder para obter uma implementação completa de amostra.
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!" ) ; Quando você configura o Watson para ouvir em 127.0.0.1 ou localhost , ele só responderá às solicitações recebidas de dentro da máquina local.
Para configurar o acesso de outros nós fora do localhost , use o seguinte:
* ou + . Você deve ser executado como administrador (limitação do sistema operacional)netsh :netsh http show urlaclnetsh http add urlacl url=http://[hostname]:[port]/ user=everyone listen=yeshostname e port são os valores que você está usando no construtor Quando você configura o Watson.lite para ouvir em 127.0.0.1 , ele só responderá às solicitações recebidas de dentro da máquina local.
Para configurar o acesso de outros nós fora da máquina local, use o seguinte:
* ou + . Você deve ser executado como administrador (limitação do sistema operacional)X509Certificate2 Consulte o Projeto Test.Docker e o arquivo Docker.md nele.
Consulte Changelog.md para obter o histórico da versão.