
Servidor web Async simple, escalable, rápido, para procesar solicitudes RESTFUL HTTP/HTTPS, escritas en C#.
| Paquete | Versión nuget | Descargas |
|---|---|---|
| Watson | ||
| Watson.lite | ||
| Watson.core |
¡Un agradecimiento especial a @DamienDennehy por permitirnos el uso del nombre del paquete Watson.Core en Nuget!
Este proyecto es parte de la Fundación .NET junto con otros proyectos como el tiempo de ejecución .NET.
SendChunk ahora acepta isFinal como una propiedad BooleanTest.ServerSentEvents ProjectMe gustaría extender un agradecimiento especial a aquellos que han ayudado a mejorar el servidor web de Watson.
Watson es un servidor web que opera encima del http.sys subyacente dentro del sistema operativo. Watson.lite se creó fusionando httpserverlite. Watson.lite no tiene una dependencia de http.sys , y se implementa utilizando una implementación de TCP proporcionada por CavemantCP.
La dependencia de http.sys (o falta de ella) crea diferencias sutiles entre las dos bibliotecas, sin embargo, la configuración y la gestión de cada una deberían ser consistentes.
Watson.lite es generalmente menos desempeñado que Watson, porque la implementación HTTP está en el espacio del usuario.
127.0.0.1 o localhostX509Certificate2 Watson y Watson.lite siempre rutas en el siguiente orden (configure usando Webserver.Routes ):
.Preflight - Manejo de solicitudes de prevergo (generalmente con OPTIONS de método HTTP).PreRouting : siempre se invoca antes de que se realice cualquier determinación de enrutamiento.PreAuthentication : un grupo de enrutamiento, compuesto por:.Static - rutas estáticas, por ejemplo, un método HTTP y una URL explícita.Content - Rutas de servicio de archivos, por ejemplo, un directorio donde se pueden leer los archivos.Parameter : rutas donde se especifican las variables en la ruta, por ejemplo /user/{id}.Dynamic - rutas donde la URL se define mediante una expresión regular.AuthenticateRequest - Ruta de demarcación entre rutas no autenticadas y autenticadas.PostAuthentication : un grupo de enrutamiento con una estructura idéntica a .PreAuthentication.Default : la ruta predeterminada; Todas las solicitudes van aquí si no se enrutan anteriormente.PostRouting : siempre invocado, generalmente para registro y telemetría Si no desea utilizar la autenticación, debe mapear sus rutas en el grupo de enrutamiento .PreAuthentication (aunque técnicamente pueden colocarse en .PostAuthentication o .Default suponiendo que el método AuthenticateRequest sea nulo.
Como regla general, nunca intente enviar datos a una HttpResponse mientras está en la ruta .PostRouting . Si ya se ha enviado una respuesta, el intento dentro de .PostRouting fracasará.
Se recomienda que implemente la autenticación en .AuthenticateRequest . Si una solicitud falla la autenticación, devuelva una respuesta dentro de esa ruta. La clase HttpContextBase tiene propiedades que pueden contener metadatos relacionados con la autenticación o relacionados con la sesión, específicamente, .Metadata .
Por defecto, Watson y Watson.lite permitirán todas las conexiones entrantes.
Server.AccessControl.DenyList.Add(ip, netmask)Server.AccessControl.Mode = AccessControlMode.DefaultDenyServer.AccessControl.PermitList.Add(ip, netmask) Consulte Test.Default para obtener un ejemplo 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!" ) ; Luego, abra su navegador a http://127.0.0.1:9000/ .
Consulte Test.Routing para obtener un ejemplo 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" ) ; Watson admite tanto recibir datos fortados como enviar datos fortados (indicados por la Transfer-Encoding: chunked ).
Consulte Test.ChunkServer para una implementación de muestra.
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 admite el envío de eventos de servidor. Consulte Test.ServerSentEvents para una implementación de muestra. El método SendEvent maneja data: y lo siguiente nn a su mensaje.
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 lo ayuda a configurar su servidor mucho más fácilmente mediante la introducción de una cadena de configuraciones y rutas en lugar de usar la clase de servidor directamente. ¡Un agradecimiento especial a @SapurtComputer30 por producir esta buena función!
Consulte Test.HostBuilder para una implementación de muestra completa.
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!" ) ; Cuando configure Watson para escuchar en 127.0.0.1 o localhost , solo responderá a las solicitudes recibidas de la máquina local.
Para configurar el acceso desde otros nodos fuera de localhost , use lo siguiente:
* o + . Debe ejecutar como administrador (limitación del sistema operativo)netsh :netsh http show urlaclnetsh http add urlacl url=http://[hostname]:[port]/ user=everyone listen=yeshostname y port son los valores que está utilizando en el constructor Cuando configure Watson.lite para escuchar en 127.0.0.1 , solo responderá a las solicitudes recibidas de la máquina local.
Para configurar el acceso desde otros nodos fuera de la máquina local, use lo siguiente:
* o + . Debe ejecutar como administrador (limitación del sistema operativo)X509Certificate2 Consulte el proyecto Test.Docker y el archivo Docker.md en el mismo.
Consulte ChangeLog.md para el historial de versiones.