Escribir a mano un contenedor http [abajo] la próxima generación de framework web progresivo

Para llevar a cabo lo anterior " Escritura a mano de un árbol de decisión y tabla de enrutamiento de contenedor http [arriba] "


  • Alojamiento de archivos estáticos: la ruta URL se asigna a la ruta FS

Una de las funciones más utilizadas del backend web es el alojamiento de archivos estáticos, es decir, aquellos archivos de solo lectura almacenados en el servidor, que se pueden descargar libremente por el front end. La forma más directa de lograr esto es mapear la ruta de la URL y la ruta del sistema de archivos uno a uno, para que pueda descargar diferentes archivos en la carpeta, incluidos los archivos en subdirectorios, a través de la URL.

Si desea crear un host estático más "generoso", puede enumerar todo el contenido del directorio cuando el front-end solicita un directorio, lo que puede enriquecer las aplicaciones del front-end en algunos casos. Basado en estas dos funciones, el código de middleware de archivos estáticos que diseñé es el siguiente (nodejs):

// 静态文件中间件的伪代码


const path = require("path");
const fs = require("fs");
const { Readable } = require("stream");
// this代表对等体的生命周期;global是全局作用域
const req = this.request;
const res = this.response;
const cfg = global.config;


// path/to/public/是FS中被托管的文件夹路径
// req.staticPath是从url中提炼出来的文件的相对路径
const absPath = path.join(__dirname, "path/to/public/", req.staticPath);
const isFile = await new Promise((resolve, reject) => {
  fs.stat(absPath, (err, stats) => {
    if (err) reject(err);
    else if (stats.isFile()) resolve(true);
    else if (stats.isDirectory()) resolve(false);
    else reject(`${req.url}既不是文件也不是文件夹!`);
  });
});


// 根据文件后缀名判断mime类型
const suffix = isFile ? path.extname(req.url) : "directory";
res.setHeader(
  "Content-Type",
  {
    ".js": "text/javascript; charset=UTF-8",
    ".wasm": "application/wasm",
    ".html": "text/html",
    ".css": "text/css",
    ".png": "image/png",
    directory: "text/plain; charset=UTF-8"
  }[suffix] || "application/octet-stream"
);
// 利用浏览器的古典缓存机制
res.setHeader("Cache-Control", `public, max-age=${cfg.cacheInSec}`);
res.setHeader("ETag", cfg.version);


let r;
if (isFile) {
  r = fs.createReadStream(absPath);
} else {
  // 如果请求的是目录,则构建一个虚拟流来读取目录下的所有内容
  r = new Readable({
    read() {}
  });
  fs.readdirSync(absPath)
    .map(name => path.join(req.staticPath, name))
    .forEach(pa => r.push(pa + "\n"));
  r.push(null);
}
if (!res.headerSent) res.writeHead();


await new Promise((resolve, reject) => {
  r.on("error", err => reject(err));
  r.on("end", resolve);
  r.pipe(res);
});


El pseudocódigo es muy simple, sin explicación redundante, el resultado es: solicitar archivos para obtener archivos; solicitar directorio para obtener una lista de rutas relativas de todos los recursos en el directorio separados por líneas nuevas; si el recurso solicitado no existe, se lanzará al punto de enrutamiento actual Ocurrió una excepción. Dado que cada punto del árbol de enrutamiento puede generar una excepción, necesitamos un mecanismo unificado de manejo de errores.


  • Mecanismo de manejo de errores y encabezado http personalizado

Es muy simple: simplemente capture la excepción al final de todo el árbol de decisión asincrónico, pero debe considerar si el momento del error se encuentra en el flujo de respuesta. Si la respuesta no se ha enviado, el mensaje de error se puede enviar al front-end como contenido; si la respuesta ya se ha enviado, o se está enviando, el back-end no puede cambiar el hecho de que se ha enviado y no puede decirle al front-end el mensaje de error. El mensaje de error puede ser digerido por el sistema de registro.

La secuencia http / 2.0 es un mecanismo abstraído del socket, y el contenido de la secuencia es el cuerpo http en lugar del encabezado. El encabezado http se utiliza para controlar el ciclo de vida del flujo. En otras palabras, los objetos de solicitud y respuesta aparecen solo después de que se pasa el encabezado.

Por tanto, los errores se pueden dividir en dos categorías según el momento de ocurrencia: antes de que se envíe la respuesta y después de que se envíe la respuesta. Si no se ha enviado la respuesta, se recomienda escribir el mensaje de error en un campo personalizado en el encabezado http, como my-error; si la respuesta se ha enviado, el mensaje de error se almacena en otro lugar.

// 决策树错误处理的伪代码


module.exports = async error => {
  const message = error.message || error || "有内鬼,终止交易";


  //   判断response是否已经发出
  if (this.response.headersSent) {
    //   将message存入假想的日志系统
    await require("path/to/logger").add(message);
  } else {
    this.response.writeHead(400, {
      "Content-Type": "text/html; charset=utf-8",
      "my-error": encodeURIComponent(message)
    });
    this.response.end(`<h1>${message}</h1>`);
  }
};


En pseudocódigo:

  • La razón para usar el sistema de registro del registrador es poner mensajes de error en un archivo de registro para que el administrador los revise.

  • La razón por la que el mensaje se escribe en el encabezado http es permitir que la interfaz lo procese por adelantado (antes del inicio de la transmisión).

  • La razón para usar la codificación URI es permitir que los caracteres Unicode se codifiquen en ASCII para escribir en el encabezado http.

  • La razón por la que el mensaje también se escribe en el cuerpo http es para evitar que el usuario no vea el mensaje de error al abrir el enlace de error directamente.


  • Analizador de cuerpo y encabezado de longitud de contenido

Los frameworks de back-end generales tendrán algunos analizadores corporales integrados como bodyParser, y escribiremos uno a mano.

La mejor manera de diseñar una barra de progreso es especificar el tamaño de todo el recurso en el primer paquete. El front-end calcula el progreso de acuerdo con el número de troncales transmitidos; si desafortunadamente no es posible saber el tamaño del recurso al principio, solo puede Escriba al lado de cada fragmento si este es el último, por supuesto, habrá espacio adicional en la sobrecarga.

El diseño de http / 2.0 también consideró estas dos situaciones, así que nos dimos el campo de longitud del contenido. El pseudocódigo no se publicará, solo tenga en cuenta que el análisis del cuerpo y otro middleware se lleva a cabo al mismo tiempo, por lo que request.body es una promesa.

------------------------ Línea divisoria irregular --------------------- ---


  • La próxima generación de framework web progresivo: FetchMe.js

Por supuesto, todavía hay muchas cosas que no se tienen en cuenta, pero eso no impide que le demos a este framework un nombre contundente: si la característica de http es "buscar", es "buscarme" para el backend, que es muy vívido.

fetchme.js es un marco web progresivo, basado en el protocolo ALFP, un andamio de próxima generación que integra una serie de conceptos avanzados de desarrollo web. Aunque el dinero actual no es suficiente, el socio está indeciso y el código no está disponible temporalmente, la base teórica y el nombre de fetchme se han predeterminado en este artículo. Con base en estas teorías, incluso si no puede ser un marco web general, al menos se puede construir una aplicación que implemente aplicaciones específicas. , Como un marco de blog.

(Terminar)


Supongo que te gusta

Origin blog.csdn.net/github_38885296/article/details/104132233
Recomendado
Clasificación