Un marco de middleware para el servidor HTTP nativo de Deno, Deno Deploy, Node.js 16.5 y más tarde, los trabajadores de Cloudflare y Bun. También incluye un enrutador de middleware.
Este marco de middleware está inspirado en el enrutador KOA y Middleware inspirado en @Koa/Router.
Este readMe se centra en la mecánica de las API de roble y está destinado a aquellos que están familiarizados con los marcos de Middleware JavaScript como Express y KOA, así como una comprensión decente de Deno. Si no está familiarizado con estos, consulte la documentación en Oakserver.github.io/oak.
Además, consulte nuestras preguntas frecuentes y el sitio increíble de recursos comunitarios.
Nota
Los ejemplos en este léase se esfuerzan de main y están diseñados para Deno CLI o Deno Deploy, lo que puede no tener sentido para hacer cuando está buscando implementar una carga de trabajo. Desea "fijar" a una versión en particular que es compatible con la versión de Deno que está utilizando y tiene un conjunto fijo de API que esperaría. https://deno.land/x/ admite el uso de etiquetas GIT en la URL para dirigirlo a una versión en particular. Entonces, para usar la versión 13.0.0 de roble, desea importar https://deno.land/x/[email protected]/mod.ts .
Oak está disponible tanto en Deno.land/x como en JSR. Para usar desde deno.land/x , importar a un módulo:
import { Application } from "https://deno.land/x/oak/mod.ts" ;Para usar desde JSR, importe en un módulo:
import { Application } from "jsr:@oak/oak@14" ;El roble está disponible para Node.js en NPM y JSR. Para usar desde NPM, instale el paquete:
npm i @oakserver/oak@14
Y luego importar a un módulo:
import { Application } from "@oakserver/oak" ;Para usar desde JSR, instale el paquete:
npx jsr i @oak/oak@14
Y luego importar a un módulo:
import { Application } from "@oak/oak/application" ; Nota
Actualmente, las actualizaciones de WebSocket y el servicio a través de TLS/HTTPS no son compatibles.
Además, el entorno y el contexto de ejecución de los trabajadores de CloudFlare no están actualmente expuestos al middleware.
Oak está disponible para los trabajadores de Cloudflare en JSR. Para usar Agregar el paquete a su proyecto de trabajadores de Cloudflare:
npx jsr add @oak/oak@14
Y luego importar a un módulo:
import { Application } from "@oak/oak/application" ;A diferencia de otros tiempos de ejecución, la aplicación de roble no escucha las solicitudes entrantes, sino que maneja las solicitudes de búsqueda de trabajadores. Un servidor de ejemplo mínimo sería:
import { Application } from "@oak/oak/application" ;
const app = new Application ( ) ;
app . use ( ( ctx ) => {
ctx . response . body = "Hello CFW!" ;
} ) ;
export default { fetch : app . fetch } ; Nota
Las actualizaciones de envío y WebSocket no son compatibles actualmente.
Oak está disponible para Bun en JSR. Para usar instalar el paquete:
bunx jsr i @oak/oak@14
Y luego importar a un módulo:
import { Application } from "@oak/oak/application" ; Nota
Las actualizaciones de envío y WebSocket no son compatibles actualmente.
La clase Application coordina la administración del servidor HTTP, ejecutar el middleware y el manejo de errores que ocurren al procesar las solicitudes. Generalmente se usan dos de los métodos: .use() y .listen() . El middleware se agrega a través del método .use() y el método .listen() iniciará el servidor e iniciará las solicitudes de procesamiento con el middleware registrado.
¡Un uso básico, respondiendo a cada solicitud con Hello World! :
import { Application } from "jsr:@oak/oak/application" ;
const app = new Application ( ) ;
app . use ( ( ctx ) => {
ctx . response . body = "Hello World!" ;
} ) ;
await app . listen ( { port : 8000 } ) ;Luego ejecutarías este guión en Deno como:
> deno run --allow-net helloWorld.ts
Para obtener más información sobre la ejecución del código en Deno, o la información sobre cómo instalar la CLI Deno, consulte el manual de Deno.
El middleware se procesa como una pila, donde cada función de middleware puede controlar el flujo de la respuesta. Cuando se llama al middleware, se pasa un contexto y referencia al método "siguiente" en la pila.
Un ejemplo más complejo:
import { Application } from "jsr:@oak/oak/application" ;
const app = new Application ( ) ;
// Logger
app . use ( async ( ctx , next ) => {
await next ( ) ;
const rt = ctx . response . headers . get ( "X-Response-Time" ) ;
console . log ( ` ${ ctx . request . method } ${ ctx . request . url } - ${ rt } ` ) ;
} ) ;
// Timing
app . use ( async ( ctx , next ) => {
const start = Date . now ( ) ;
await next ( ) ;
const ms = Date . now ( ) - start ;
ctx . response . headers . set ( "X-Response-Time" , ` ${ ms } ms` ) ;
} ) ;
// Hello World!
app . use ( ( ctx ) => {
ctx . response . body = "Hello World!" ;
} ) ;
await app . listen ( { port : 8000 } ) ; Para proporcionar un servidor HTTPS, las opciones app.listen() deben incluir las opciones .secure Opción Secure establecida en true y suministrar opciones .certFile y A .keyFile también.
.handle() método El método .handle() se utiliza para procesar solicitudes y recibir respuestas sin que la aplicación administre el aspecto del servidor. Sin embargo, esto es un uso avanzado y la mayoría de los usuarios querrán usar .listen() .
El método .handle() acepta hasta tres argumentos. El primero es un argumento Request , y el segundo es un argumento Deno.Conn . El tercer argumento opcional es una bandera para indicar si la solicitud era "segura" en el sentido de que se originó desde una conexión TLS al cliente remoto. El método se resolvió con un objeto Response o undefined si el ctx.respond === true .
Un ejemplo:
import { Application } from "jsr:@oak/oak/application" ;
const app = new Application ( ) ;
app . use ( ( ctx ) => {
ctx . response . body = "Hello World!" ;
} ) ;
const listener = Deno . listen ( { hostname : "localhost" , port : 8000 } ) ;
for await ( const conn of listener ) {
( async ( ) => {
const requests = Deno . serveHttp ( conn ) ;
for await ( const { request , respondWith } of requests ) {
const response = await app . handle ( request , conn ) ;
if ( response ) {
respondWith ( response ) ;
}
}
} ) ;
}Una instancia de aplicación también tiene algunas propiedades:
contextState : determina el método utilizado para crear estado para un nuevo contexto. Un valor de "clone" establecerá el estado como un clon del estado de la aplicación. Un valor de "prototype" significa que el estado de la aplicación se utilizará como prototipo del estado del contexto. Un valor de "alias" significa que el estado de la aplicación y el estado del contexto serán una referencia al mismo objeto. Un valor de "empty" inicializará el estado del contexto con un objeto vacío.
.jsonBodyReplacer : una función de reemplazo opcional que se aplicará a los cuerpos JSON al formar una respuesta.
.jsonBodyReviver : una función reviver opcional que se aplicará al leer los cuerpos JSON en una solicitud.
.keys
Claves para usarse al firmar y verificar cookies. El valor se puede establecer en una matriz de claves e instancia de KeyStack , o un objeto que proporciona la misma interfaz que KeyStack (por ejemplo, una instancia de KeyGrip). Si solo se pasan las claves, Oak administrará las claves a través de KeyStack lo que permite una rotación de teclas fácil sin requerir la re-firmación de los valores de datos.
.proxy
Este valor predeterminado en false , pero se puede configurar a través de las opciones de constructor Application . Esto está destinado a indicar que la aplicación está detrás de un proxy y usará X-Forwarded-Proto , X-Forwarded-Host y X-Forwarded-For procesar la solicitud, que debería proporcionar información más precisa sobre la solicitud.
.state
Un registro del estado de aplicación, que puede escribirse fuertemente especificando un argumento genérico al construir una Application() , o inferirse al pasar un objeto de estado (p. Ej Application({ state }) ).
El contexto que se pasa al middleware tiene varias propiedades:
.app
Una referencia a la Application que está invocando este middleware.
.cookies
La instancia Cookies para este contexto que le permite leer y establecer cookies.
.request
El objeto Request que contiene detalles sobre la solicitud.
.respond
Determina si cuando el middleware finaliza el procesamiento, la aplicación debe enviar la .response al cliente. Si true , la respuesta se enviará, y si false la respuesta no se enviará. El valor predeterminado es true pero ciertos métodos, como .upgrade() y .sendEvents() establecerán esto en false .
.response
El objeto Response que se usará para formar la respuesta enviada de vuelta al solicitante.
.socket
Esto estará undefined si la conexión no se ha actualizado a un socket web. Si la conexión se ha actualizado, se establecerá la interfaz .socket .
.state
Un registro del estado de aplicación, que puede escribirse fuertemente especificando un argumento genérico al construir una Application() , o inferirse al pasar un objeto de estado (p. Ej Application({ state }) ).
El contexto que se pasa al middleware tiene algunos métodos:
.assert()
Hace una afirmación, que, si no es cierto, arroja un HTTPError , que la subclase está identificada por el segundo argumento, siendo el mensaje el tercer argumento.
.send()
Transmitir un archivo al cliente solicitante. Consulte el contenido estático a continuación para obtener más información.
.sendEvents()
Convierta la conexión actual en una respuesta de evento de Servidor y devuelve un ServerSentEventTarget donde los mensajes y eventos se pueden transmitir al cliente. Esto se establecerá .respond Responder a false .
.throw()
Lanza un HTTPError , que la subclase está identificada por el primer argumento, con el mensaje que se pasa como el segundo.
.upgrade()
Intente actualizar la conexión a una conexión de socket web y devuelva una interfaz WebSocket . Versión anterior de Oak, esta sería una Promise de resolver con un socket web std/ws .
A diferencia de otros marcos de middleware, context no tiene una cantidad significativa de alias. La información sobre la solicitud solo se encuentra en .request y la información sobre la respuesta solo se encuentra en .response .
El context.cookies permite el acceso a los valores de las cookies en la solicitud, y permite que las cookies se establezcan en la respuesta. Asegura automáticamente las cookies si la propiedad .keys se establece en la aplicación. Debido a que .cookies usa las API criptográficas web para firmar y validar cookies, y esas API funcionan de manera asincrónica, las API de cookies funcionan de manera asincrónica. Tiene varios métodos:
.get(key: string, options?: CookieGetOptions): Promise<string | undefined>
Intenta recuperar la cookie de la solicitud y devuelve el valor de la cookie en función de la clave. Si se establecen las aplicaciones .keys , entonces la cookie se verificará contra una versión firmada de la cookie. Si la cookie es válida, la promesa se resolverá con el valor. Si no es válido, la firma de las cookies se configurará para eliminar la respuesta. Si la clave actual no firmó la cookie, se renunciará y se agregará a la respuesta.
.set(key: string, value: string, options?: CookieSetDeleteOptions): Promise<void>
Establecerá una cookie en la respuesta en función de la clave proporcionada, el valor y cualquier opción. Si se establecen las aplicaciones .keys , entonces la cookie se firmará y la firma se agregará a la respuesta. A medida que las claves se firman de manera asincrónica, se recomienda esperar el método .set() .
El context.request contiene información sobre la solicitud. Contiene varias propiedades:
.body
Un objeto que proporciona acceso al cuerpo de la solicitud. Consulte a continuación para obtener detalles sobre la API del cuerpo de solicitud.
.hasBody
Establecer en true si la solicitud puede tener un cuerpo o false si no lo hace. Sin embargo, no valida si el cuerpo es soportado por el analizador de cuerpo incorporado.
[! Advertencia] Esta es una API poco confiable. En HTTP/2 en muchas situaciones no puede determinar si una solicitud tiene un cuerpo o no a menos que intente leer el cuerpo, debido a la naturaleza de transmisión de HTTP/2. A partir de Deno 1.16.1, para HTTP/1.1, Deno también refleja ese comportamiento. La única forma confiable de determinar si una solicitud tiene un cuerpo o no es intentar leer el cuerpo.
Es mejor determinar si un cuerpo podría ser significativo para usted con un método dado, y luego intentar leer y procesar el cuerpo si es significativo en un contexto dado. Por ejemplo, GET and HEAD nunca debe tener un cuerpo, pero métodos como DELETE y OPTIONS pueden tener un cuerpo y deben tener su cuerpo procesado condicionalmente si es significativo para su aplicación.
.headers
Los encabezados para la solicitud, una instancia de Headers .
.method
Una cadena que representa el método HTTP para la solicitud.
.originalRequest
Descargado esto se eliminará en una liberación futura de roble.
La solicitud NativeServer "sin procesar", que es una abstracción sobre el objeto Request DOM. .originalRequest.request es la instancia Request DOM que se está procesando. Los usuarios generalmente deben evitar usarlos.
.secure
Un atajo para .protocol , devolviendo true si https de otra manera false .
.source
Cuando se ejecute bajo Deno, .source se establecerá en la Request de estándar web de origen. Cuando se ejecuta en NodeJS, esto estará undefined .
.url
Una instancia de URL que se basa en la URL completa para la solicitud. Esto está en lugar de tener partes de la URL expuestas en el resto del objeto de solicitud.
Y varios métodos:
.accepts(...types: string[])
Negocia el tipo de contenido respaldado por la solicitud de respuesta. Si no se aproban tipos de contenido, el método devuelve una matriz priorizada de tipos de contenido aceptado. Si se pasan los tipos de contenido, se devuelve el mejor tipo de contenido negociado. Si no se devuelve el tipo de contenido undefined .
.acceptsEncodings(...encodings: string[])
Negocia la codificación de contenido respaldada por la solicitud de respuesta. Si no se aproban codificaciones, el método devuelve una matriz priorizada de codificaciones aceptadas. Si se pasan codificaciones, se devuelve la mejor codificación negociada. Si no se devuelve las codificaciones que coinciden undefined .
.acceptsLanguages(...languages: string[])
Negocia el idioma que el cliente puede entender. Donde una variante local toma preferencia. Si no se pasan codificaciones, el método devuelve una matriz priorizada de idiomas entendidos. Si se aprueban los idiomas, se devuelve el mejor idioma negociado. Si no se devuelve los idiomas que coinciden undefined .
Importante
Esta API cambió significativamente en el roble V13 y posterior. La API anterior había crecido orgánicamente desde que Oak fue creado en 2018 y no representaba ninguna otra API común. La API introducida en V13 se alinea mejor con la forma Request de la API de Fetch de tratar con el cuerpo, y debería ser más familiar para los desarrolladores que vienen a roble por primera vez.
La API para la solicitud de roble .body El cuerpo está inspirado en la Request de la API de Fetch pero con alguna funcionalidad de agregar. El contexto de request.body El cuerpo es una instancia de un objeto que proporciona varias propiedades:
.has
Establecer en true si la solicitud puede tener un cuerpo o false si no lo hace. Sin embargo, no valida si el cuerpo es soportado por el analizador de cuerpo incorporado.
[! IMPORTANTE] Esta es una API poco confiable. En HTTP/2 en muchas situaciones no puede determinar si una solicitud tiene un cuerpo o no a menos que intente leer el cuerpo, debido a la naturaleza de transmisión de HTTP/2. A partir de Deno 1.16.1, para HTTP/1.1, Deno también refleja ese comportamiento. La única forma confiable de determinar si una solicitud tiene un cuerpo o no es intentar leer el cuerpo.
Es mejor determinar si un cuerpo podría ser significativo para usted con un método dado, y luego intentar leer y procesar el cuerpo si es significativo en un contexto dado. Por ejemplo, GET and HEAD nunca debe tener un cuerpo, pero métodos como DELETE y OPTIONS pueden tener un cuerpo y deben tener su cuerpo procesado condicionalmente si es significativo para su aplicación.
.stream
Un ReadableStream<Uint8Array> que permitirá la lectura del cuerpo en los trozos Uint8Array . Esta es su propiedad .body en una Request de API de Fetch.
.used
Establecer en true si el cuerpo se ha utilizado, de lo contrario establecido en false .
También tiene varios métodos:
arrayBuffer()
Se resuelve con un ArrayBuffer que contiene el contenido del cuerpo, si es que hay. Adecuado para leer/manejar datos binarios.
blob()
Se resuelve con una Blob que contiene el contenido del cuerpo. Adecuado para leer/manejar datos binarios.
form()
Se resuelve con un URLSearchParams que ha sido decodificado del contenido de un cuerpo. Esto es apropiado para un cuerpo con un tipo de contenido de application/x-www-form-urlencoded .
formData()
Se resuelve con una instancia FormData que ha sido decodificada del contenido de un cuerpo. Esto es apropiado para un cuerpo con un tipo de contenido de multipart/form-data .
json()
Se resuelve con los datos del cuerpo analizado como JSON. Si se ha especificado un jsonBodyReviver en la aplicación, se usará al analizar el JSON.
text()
Se resuelve con una cadena que representa el contenido del cuerpo.
type()
Intenta proporcionar orientación de cómo se codifica el cuerpo que se puede usar para determinar qué método usar para decodificar el cuerpo. El método devuelve una cadena que representa el tipo de cuerpo interpretado:
| Valor | Descripción |
|---|---|
"binary" | El cuerpo tiene un tipo de contenido que indica datos binarios y el .arrayBuffer() , .blob() o .stream debe usarse para leer el cuerpo. |
"form" | El cuerpo está codificado como datos de formulario y .form() debe usarse para leer el cuerpo. |
"form-data" | El cuerpo está codificado como una forma de múltiples partes y .formData() debe usarse para leer el cuerpo. |
"json" | El cuerpo está codificado como datos JSON y .json() debe usarse para leer el cuerpo. |
"text" | El cuerpo está codificado como texto y .text() debe usarse para leer el cuerpo. |
"unknown" | O no hay cuerpo o no fue posible determinar el tipo de cuerpo en función del tipo de contenido. |
El método .type() también toma un argumento opcional de los tipos de medios personalizados que se utilizarán al intentar determinar el tipo de cuerpo. Luego se incorporan a los tipos de medios predeterminados. El valor es un objeto donde la clave es una de binary , form , form-data , json o text y el valor es el tipo de medio apropiado en un formato compatible con el formato tipo-IS.
El context.response contiene información sobre la respuesta que se enviará al solicitante. Contiene varias propiedades:
.body
El cuerpo de la respuesta, que a menudo puede ser manejado por el manejo del cuerpo de respuesta automática documentado a continuación.
.headers
Una instancia Headers que contiene los encabezados para la respuesta.
.status
Un código Status HTTP que se enviará de regreso con la respuesta. Si esto no se establece antes de responder, OAK se debe poner en cuenta a 200 OK si hay un .body , de lo contrario 404 Not Found .
.type
Un tipo de medio o extensión para establecer el encabezado Content-Type para la respuesta. Por ejemplo, puede proporcionar txt o text/plain para describir el cuerpo.
Y varios métodos:
.redirect(url?: string | URL | REDIRECT_BACK, alt?: string | URL)
Un método para simplificar la redirección de la respuesta a otra URL. Establecerá el encabezado Location en la url suministrada y el estado en 302 Found (a menos que el estado ya sea un estado 3XX ). El uso del símbolo REDIRECT_BACK como la url indica que el encabezado de Referer en la solicitud debe usarse como la dirección, siendo alt la ubicación alternativa si el Referer no está configurado. Si ni el alt ni el Referer están establecidos, la redirección se producirá a / . Un HTML básico (si el solicitante lo admite) o un cuerpo de texto se establecerá explicando que están siendo redirigidos.
.toDomResponse()
Esto convierte la información que Oak entiende sobre la respuesta a la Response de la API de Fetch. Esto finalizará la respuesta, lo que resulta en cualquier intento adicional de modificar la respuesta a lanzar. Esto está destinado a usarse internamente dentro de Oak para poder responder a las solicitudes.
.with(response: Response) y .with(body?: BodyInit, init?: ResponseInit)
Esto establece la respuesta a una Response estándar web. Tenga en cuenta que esto ignorará/anulará cualquier otra información establecida en la respuesta de otros middleware, incluidas cosas como encabezados o cookies que se establecerán.
Cuando el Content-Type de respuesta no se establece en los encabezados de la .response , Oak intentará determinar automáticamente el Content-Type apropiado. Primero se verá .response.type . Si se le asigna, intentará resolver el tipo de medio apropiado en función del tratamiento del valor de .type como el tipo de medios o resolver el tipo de medios en función de una extensión. Por ejemplo, si .type se estableció en "html" , entonces el Content-Type se establecerá en "text/html" .
Si .type no se establece con un valor, entonces Oak inspeccionará el valor de .response.body . Si el valor es una string , entonces OAK verificará si la cadena parece HTML, si es así, Content-Type se establecerá en text/html de lo contrario se establecerá en text/plain . Si el valor es un objeto, que no sea Uint8Array , un Deno.Reader o null , el objeto se pasará a JSON.stringify() y el Content-Type se establecerá en application/json .
Si el tipo de cuerpo es un número, bigint o símbolo, se coaccionará a una cadena y se tratará como texto.
Si el valor del cuerpo es una función, la función se llamará sin argumentos. Si el valor de retorno de la función es prometedor, eso será esperado y el valor resuelto se procesará como se indicó anteriormente. Si el valor no es prometedor, se procesará como se indicó anteriormente.
El método de aplicación .listen() se utiliza para abrir el servidor, comenzar a escuchar las solicitudes y procesar el middleware registrado para cada solicitud. Este método devuelve una promesa cuando se cierra el servidor.
Una vez que el servidor está abierto, antes de que comience las solicitudes de procesamiento, la aplicación despedirá un evento de "listen" , que puede ser escuchado a través del método .addEventListener() . Por ejemplo:
import { Application } from "jsr:@oak/oak/application" ;
const app = new Application ( ) ;
app . addEventListener ( "listen" , ( { hostname , port , secure } ) => {
console . log (
`Listening on: ${ secure ? "https://" : "http://" } ${
hostname ?? "localhost"
} : ${ port } ` ,
) ;
} ) ;
// register some middleware
await app . listen ( { port : 80 } ) ;Si desea cerrar la aplicación, la aplicación admite la opción de una señal de aborto. Aquí hay un ejemplo de uso de la señal:
import { Application } from "jsr:@oak/oak/application" ;
const app = new Application ( ) ;
const controller = new AbortController ( ) ;
const { signal } = controller ;
// Add some middleware using `app.use()`
const listenPromise = app . listen ( { port : 8000 , signal } ) ;
// In order to close the server...
controller . abort ( ) ;
// Listen will stop listening for requests and the promise will resolve...
await listenPromise ;
// and you can do something after the close to shutdownEl middleware se puede usar para manejar otros errores con middleware. Esperar otro middleware para ejecutar mientras funciona los errores de captura. Entonces, si tuviera un error de manejo de middleware que proporcione una respuesta bien administrada a los errores, funcionaría así:
import { Application } from "jsr:@oak/oak/application" ;
import { isHttpError } from "jsr:@oak/commons/http_errors" ;
import { Status } from "jsr:@oak/commons/status" ;
const app = new Application ( ) ;
app . use ( async ( ctx , next ) => {
try {
await next ( ) ;
} catch ( err ) {
if ( isHttpError ( err ) ) {
switch ( err . status ) {
case Status . NotFound :
// handle NotFound
break ;
default :
// handle other statuses
}
} else {
// rethrow if you can't handle the error
throw err ;
}
}
} ) ; Las excepciones de middleware no capturas serán atrapadas por la aplicación. Application extiende el Global EventTarget en Deno, y cuando los errores no capturados ocurren en el middleware o el envío de respuestas, se enviará un EventError a la aplicación. Para escuchar estos errores, agregaría un controlador de eventos a la instancia de la aplicación:
import { Application } from "jsr:@oak/oak/application" ;
const app = new Application ( ) ;
app . addEventListener ( "error" , ( evt ) => {
// Will log the thrown error to the console.
console . log ( evt . error ) ;
} ) ;
app . use ( ( ctx ) => {
// Will throw a 500 on every request.
ctx . throw ( 500 ) ;
} ) ;
await app . listen ( { port : 80 } ) ; La clase de Router produce un middleware que se puede usar con una Application para habilitar el enrutamiento en función del nombre de ruta de la solicitud.
El siguiente ejemplo sirve un servicio relajante de un mapa de libros, donde http://localhost:8000/book/ devolverá una matriz de libros y http://localhost:8000/book/1 devolvería el libro con identificación "1" :
import { Application } from "jsr:@oak/oak/application" ;
import { Router } from "jsr:@oak/oak/router" ;
const books = new Map < string , any > ( ) ;
books . set ( "1" , {
id : "1" ,
title : "The Hound of the Baskervilles" ,
author : "Conan Doyle, Arthur" ,
} ) ;
const router = new Router ( ) ;
router
. get ( "/" , ( context ) => {
context . response . body = "Hello world!" ;
} )
. get ( "/book" , ( context ) => {
context . response . body = Array . from ( books . values ( ) ) ;
} )
. get ( "/book/:id" , ( context ) => {
if ( books . has ( context ?. params ?. id ) ) {
context . response . body = books . get ( context . params . id ) ;
}
} ) ;
const app = new Application ( ) ;
app . use ( router . routes ( ) ) ;
app . use ( router . allowedMethods ( ) ) ;
await app . listen ( { port : 8000 } ) ; Una ruta pasada se convierte en una expresión regular utilizando la ruta a la regexp, lo que significa que se convertirán los parámetros expresados en el patrón. path-to-regexp tiene un uso avanzado que puede crear patrones complejos que se pueden usar para la coincidencia. Consulte la documentación de esa biblioteca si tiene casos de uso avanzados.
En la mayoría de los casos, el tipo de context.params Params se infiere automáticamente de la cadena de plantilla de ruta a través de Magic de TypeScript. Sin embargo, en escenarios más complejos, esto podría no dar el resultado correcto. En ese caso, puede anular el tipo con router.get<RouteParams> , donde RouteParams es el tipo explícito para context.params .
Los enrutadores de anidación son compatibles. El siguiente ejemplo responde a http://localhost:8000/forums/oak/posts y http://localhost:8000/forums/oak/posts/nested-routers .
import { Application } from "jsr:@oak/oak/application" ;
import { Router } from "jsr:@oak/oak/router" ;
const posts = new Router ( )
. get ( "/" , ( ctx ) => {
ctx . response . body = `Forum: ${ ctx . params . forumId } ` ;
} )
. get ( "/:postId" , ( ctx ) => {
ctx . response . body =
`Forum: ${ ctx . params . forumId } , Post: ${ ctx . params . postId } ` ;
} ) ;
const forums = new Router ( ) . use (
"/forums/:forumId/posts" ,
posts . routes ( ) ,
posts . allowedMethods ( ) ,
) ;
await new Application ( ) . use ( forums . routes ( ) ) . listen ( { port : 8000 } ) ; La función send() está diseñada para servir contenido estático como parte de una función de middleware. En el uso más directo, se proporciona una raíz y las solicitudes proporcionadas a la función se cumplen con los archivos del sistema de archivos local en relación con la raíz de la ruta solicitada.
Un uso básico se vería algo así:
import { Application } from "jsr:@oak/oak/application" ;
const app = new Application ( ) ;
app . use ( async ( context , next ) => {
try {
await context . send ( {
root : ` ${ Deno . cwd ( ) } /examples/static` ,
index : "index.html" ,
} ) ;
} catch {
await next ( ) ;
}
} ) ;
await app . listen ( { port : 8000 } ) ; send() admite automáticamente características como proporcionar encabezados ETag y Last-Modified en la respuesta, así como el procesamiento de encabezados If-None-Match e If-Modified-Since en la solicitud. Esto significa que al servir contenido estático, los clientes podrán confiar en sus versiones en caché de activos en lugar de volver a descargarlos.
El método send() admite automáticamente la generación de un encabezado ETag para activos estáticos. El encabezado permite al cliente determinar si necesita descargar un activo o no, pero puede ser útil calcular ETag S para otros escenarios.
Existe una función de middleware que evalúa el context.reponse.body Reponse.body y determina si puede crear un encabezado ETag para ese tipo de cuerpo, y si es así, establece el encabezado ETag en la respuesta. El uso básico se vería algo así:
import { Application } from "jsr:@oak/oak/application" ;
import { factory } from "jsr:@oak/oak/etag" ;
const app = new Application ( ) ;
app . use ( factory ( ) ) ;
// ... other middleware for the applicationTambién hay una función que recupera una entidad para un contexto dado basado en lo que es lógico leer en la memoria que puede pasar a la calculación de ETAG que es parte de la biblioteca Deno STD:
import { Application } from "jsr:@oak/oak/application" ;
import { getEntity } from "jsr:@oak/oak/etag" ;
import { calculate } from "jsr:@std/http/etag" ;
const app = new Application ( ) ;
// The context.response.body has already been set...
app . use ( async ( ctx ) => {
const entity = await getEntity ( ctx ) ;
if ( entity ) {
const etag = await calculate ( entity ) ;
}
} ) ; Deno.serve() Si está migrando desde Deno.serve() o el código de adaptación que está diseñado para la Request y Response de la API de Fetch estándar web, hay un par de características de roble para ayudar.
ctx.request.source Cuando se ejecuta bajo Deno, esto se establecerá en una Request de API de Fetch, dando acceso directo a la solicitud original.
ctx.response.with() Este método aceptará una Response de API de Fetch o creará una nueva respuesta basada en el BodyInit y ResponseInit proporcionados. Esto también finalizará la respuesta e ignora cualquier cosa que pueda haberse establecido en la .response roble.
middleware/serve#serve() y middelware/serve#route() Estos dos generadores de middleware se pueden usar para adaptar el código que funciona más como el Deno.serve() en el sentido de que proporciona una Request de API de Fetch y espera que el controlador se resuelva con una Response API de Fetch.
Un ejemplo de uso de serve() con Application.prototype.use() ::
import { Application } from "jsr:@oak/oak/application" ;
import { serve } from "jsr:@oak/oak/serve" ;
const app = new Application ( ) ;
app . use ( serve ( ( req , ctx ) => {
console . log ( req . url ) ;
return new Response ( "Hello world!" ) ;
} ) ) ;
app . listen ( ) ; Y una solución similar funciona con route() donde el contexto contiene la información sobre el enrutador, como los parámetros:
import { Application } from "jsr:@oak/oak/application" ;
import { Router } from "jsr:@oak/oak/router" ;
import { route } from "jsr:@oak/oak/serve" ;
const app = new Application ;
const router = new Router ( ) ;
router . get ( "/books/:id" , route ( ( req , ctx ) => {
console . log ( ctx . params . id ) ;
return Response . json ( { title : "hello world" , id : ctx . params . id } ) ;
} ) ) ;
app . use ( router . routes ( ) ) ;
app . listen ( ) ; El mod.ts exporta un objeto llamado testing que contiene algunas utilidades para probar el middleware de roble que puede crear. Consulte la prueba con roble para obtener más información.
Hay varios módulos que se adaptan directamente de otros módulos. Han conservado sus licencias individuales y derechos de autor. Todos los módulos, incluidos los directamente adaptados, tienen licencia bajo la licencia MIT.
Todo el trabajo adicional es Copyright 2018 - 2024 Los autores de roble. Reservados todos los derechos.