Изчерпателно ръководство за разбиране и прилагане на TypeScript middleware в приложенията на Express.js. Разгледайте усъвършенствани типови шаблони за стабилен и поддържан код.
TypeScript Middleware: Овладяване на типовите шаблони за middleware в Express
Express.js, минималистичен и гъвкав Node.js framework за уеб приложения, позволява на разработчиците да изградят стабилни и мащабируеми API и уеб приложения. TypeScript подобрява Express, като добавя статична типизация, подобрява поддръжката на кода и хваща грешките рано. Middleware функциите са крайъгълен камък на Express, което ви позволява да прихващате и обработвате заявките, преди да достигнат вашите обработчици на маршрути. Тази статия разглежда усъвършенствани типови шаблони на TypeScript за дефиниране и използване на Express middleware, подобрявайки типовата безопасност и яснотата на кода.
Разбиране на Express Middleware
Middleware функциите са функции, които имат достъп до обекта на заявката (req), обекта на отговора (res) и следващата middleware функция в цикъла на заявка-отговор на приложението. Middleware функциите могат да извършват следните задачи:
- Изпълнение на код.
- Внасяне на промени в обектите на заявката и отговора.
- Прекратяване на цикъла на заявка-отговор.
- Извикване на следващата middleware функция в стека.
Middleware функциите се изпълняват последователно, когато бъдат добавени към приложението Express. Често срещаните случаи на употреба за middleware включват:
- Регистриране на заявки.
- Удостоверяване на потребители.
- Оторизиране на достъп до ресурси.
- Валидиране на данни от заявки.
- Обработка на грешки.
Основен TypeScript Middleware
В основно TypeScript Express приложение, middleware функцията може да изглежда така:
import { Request, Response, NextFunction } from 'express';
function loggerMiddleware(req: Request, res: Response, next: NextFunction) {
console.log(`Request: ${req.method} ${req.url}`);
next();
}
export default loggerMiddleware;
Този прост middleware регистрира метода на заявката и URL адреса в конзолата. Нека разгледаме типовите анотации:
Request: Представя обекта на заявката Express.Response: Представя обекта на отговора Express.NextFunction: Функция, която, когато бъде извикана, изпълнява следващата middleware в стека.
Можете да използвате този middleware във вашето Express приложение по следния начин:
import express from 'express';
import loggerMiddleware from './middleware/loggerMiddleware';
const app = express();
const port = 3000;
app.use(loggerMiddleware);
app.get('/', (req, res) => {
res.send('Hello, world!');
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
Усъвършенствани типови шаблони за Middleware
Докато основният пример за middleware е функционален, той няма гъвкавост и типова безопасност за по-сложни сценарии. Нека разгледаме усъвършенствани типови шаблони, които подобряват разработването на middleware с TypeScript.
1. Персонализирани типове заявки/отговори
Често ще трябва да разширите обектите Request или Response с потребителски свойства. Например, след удостоверяване, може да искате да добавите свойството user към обекта Request. TypeScript ви позволява да увеличите съществуващите типове, като използвате сливане на декларации.
// src/types/express/index.d.ts
import { Request as ExpressRequest } from 'express';
declare global {
namespace Express {
interface Request {
user?: {
id: string;
email: string;
// ... other user properties
};
}
}
}
export {}; // This is needed to make the file a module
В този пример увеличаваме интерфейса Express.Request, за да включим незадължително свойство user. Сега, във вашия middleware за удостоверяване, можете да попълните това свойство:
import { Request, Response, NextFunction } from 'express';
function authenticationMiddleware(req: Request, res: Response, next: NextFunction) {
// Simulate authentication logic
const userId = req.headers['x-user-id'] as string; // Or fetch from a token, etc.
if (userId) {
// In a real application, you would fetch the user from a database
req.user = {
id: userId,
email: `user${userId}@example.com`
};
next();
} else {
res.status(401).send('Unauthorized');
}
}
export default authenticationMiddleware;
И във вашите обработчици на маршрути можете спокойно да получите достъп до свойството req.user:
import express from 'express';
import authenticationMiddleware from './middleware/authenticationMiddleware';
const app = express();
const port = 3000;
app.use(authenticationMiddleware);
app.get('/profile', (req: Request, res: Response) => {
if (req.user) {
res.send(`Hello, ${req.user.email}! Your user ID is ${req.user.id}`);
} else {
// This should never happen if the middleware is working correctly
res.status(500).send('Internal Server Error');
}
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
2. Middleware фабрики
Middleware фабриките са функции, които връщат middleware функции. Този модел е полезен, когато трябва да конфигурирате middleware с конкретни опции или зависимости. Например, помислете за middleware за регистриране, което регистрира съобщения в конкретен файл:
import { Request, Response, NextFunction } from 'express';
import fs from 'fs';
import path from 'path';
function createLoggingMiddleware(logFilePath: string) {
return (req: Request, res: Response, next: NextFunction) => {
const logMessage = `[${new Date().toISOString()}] Request: ${req.method} ${req.url}\n`;
fs.appendFile(logFilePath, logMessage, (err) => {
if (err) {
console.error('Error writing to log file:', err);
}
next();
});
};
}
export default createLoggingMiddleware;
Можете да използвате тази middleware фабрика по следния начин:
import express from 'express';
import createLoggingMiddleware from './middleware/loggingMiddleware';
const app = express();
const port = 3000;
const logFilePath = path.join(__dirname, 'logs', 'requests.log');
app.use(createLoggingMiddleware(logFilePath));
app.get('/', (req, res) => {
res.send('Hello, world!');
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
3. Асинхронен Middleware
Middleware функциите често трябва да изпълняват асинхронни операции, като заявки към база данни или API извиквания. За да обработвате асинхронни операции правилно, трябва да се уверите, че функцията next е извикана след като асинхронната операция приключи. Можете да постигнете това, като използвате async/await или Promises.
import { Request, Response, NextFunction } from 'express';
async function asyncMiddleware(req: Request, res: Response, next: NextFunction) {
try {
// Simulate an asynchronous operation
await new Promise(resolve => setTimeout(resolve, 100));
console.log('Asynchronous operation completed');
next();
} catch (error) {
next(error); // Pass the error to the error handling middleware
}
}
export default asyncMiddleware;
Важно: Не забравяйте да обработвате грешките във вашия асинхронен middleware и да ги предадете на middleware за обработка на грешки, като използвате next(error). Това гарантира, че грешките са правилно обработени и регистрирани.
4. Middleware за обработка на грешки
Middleware за обработка на грешки е специален тип middleware, който обработва грешки, възникнали по време на цикъла заявка-отговор. Функциите на middleware за обработка на грешки имат четири аргумента: err, req, res и next.
import { Request, Response, NextFunction } from 'express';
function errorHandler(err: any, req: Request, res: Response, next: NextFunction) {
console.error(err.stack);
res.status(500).send('Something went wrong!');
}
export default errorHandler;
Трябва да регистрирате middleware за обработка на грешки след всички други middleware и обработчици на маршрути. Express идентифицира middleware за обработка на грешки по наличието на четирите аргумента.
import express from 'express';
import asyncMiddleware from './middleware/asyncMiddleware';
import errorHandler from './middleware/errorHandler';
const app = express();
const port = 3000;
app.use(asyncMiddleware);
app.get('/', (req, res) => {
throw new Error('Simulated error!'); // Simulate an error
});
app.use(errorHandler); // Error handling middleware MUST be registered last
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
5. Middleware за валидиране на заявки
Валидирането на заявките е решаващ аспект от изграждането на сигурни и надеждни API. Middleware може да се използва за валидиране на входящи данни от заявки и да се гарантира, че те отговарят на определени критерии, преди да достигнат вашите обработчици на маршрути. Библиотеки като joi или express-validator могат да се използват за валидиране на заявки.
Ето един пример, използващ express-validator:
import { Request, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
const validateCreateUserRequest = [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
}
];
export default validateCreateUserRequest;
Този middleware валидира полетата email и password в тялото на заявката. Ако валидирането не успее, връща отговор 400 Bad Request с масив от съобщения за грешки. Можете да използвате този middleware във вашите обработчици на маршрути по следния начин:
import express from 'express';
import validateCreateUserRequest from './middleware/validateCreateUserRequest';
const app = express();
const port = 3000;
app.post('/users', validateCreateUserRequest, (req, res) => {
// If validation passes, create the user
res.send('User created successfully!');
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
6. Инжектиране на зависимости за Middleware
Когато вашите middleware функции зависят от външни услуги или конфигурации, инжектирането на зависимости може да помогне за подобряване на възможността за тестване и поддръжка. Можете да използвате контейнер за инжектиране на зависимости като tsyringe или просто да предадете зависимостите като аргументи на вашите middleware фабрики.
Ето пример, използващ middleware фабрика с инжектиране на зависимости:
// src/services/UserService.ts
export class UserService {
async createUser(email: string, password: string): Promise {
// In a real application, you would save the user to a database
console.log(`Creating user with email: ${email} and password: ${password}`);
await new Promise(resolve => setTimeout(resolve, 500)); // Simulate a database operation
}
}
// src/middleware/createUserMiddleware.ts
import { Request, Response, NextFunction } from 'express';
import { UserService } from '../services/UserService';
function createCreateUserMiddleware(userService: UserService) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const { email, password } = req.body;
await userService.createUser(email, password);
res.status(201).send('User created successfully!');
} catch (error) {
next(error);
}
};
}
export default createCreateUserMiddleware;
// src/app.ts
import express from 'express';
import createCreateUserMiddleware from './middleware/createUserMiddleware';
import { UserService } from './services/UserService';
import errorHandler from './middleware/errorHandler';
const app = express();
const port = 3000;
app.use(express.json()); // Parse JSON request bodies
const userService = new UserService();
const createUserMiddleware = createCreateUserMiddleware(userService);
app.post('/users', createUserMiddleware);
app.use(errorHandler);
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
Най-добри практики за TypeScript Middleware
- Поддържайте middleware функциите малки и фокусирани. Всяка middleware функция трябва да има една отговорност.
- Използвайте описателни имена за вашите middleware функции. Името трябва ясно да показва какво прави middleware.
- Обработвайте грешките правилно. Винаги хващайте грешките и ги предавайте на middleware за обработка на грешки, като използвате
next(error). - Използвайте персонализирани типове заявки/отговори за подобряване на типовата безопасност. Увеличете интерфейсите
RequestиResponseс персонализирани свойства, ако е необходимо. - Използвайте middleware фабрики за конфигуриране на middleware със специфични опции.
- Документирайте вашите middleware функции. Обяснете какво прави middleware и как трябва да се използва.
- Тествайте старателно вашите middleware функции. Напишете unit тестове, за да се уверите, че вашите middleware функции работят правилно.
Заключение
TypeScript значително подобрява разработването на Express middleware, като добавя статична типизация, подобрява поддръжката на кода и хваща грешките рано. Чрез овладяване на усъвършенствани типови шаблони като персонализирани типове заявки/отговори, middleware фабрики, асинхронен middleware, middleware за обработка на грешки и middleware за валидиране на заявки, можете да изградите стабилни, мащабируеми и типово-безопасни Express приложения. Не забравяйте да следвате най-добрите практики, за да запазите вашите middleware функции малки, фокусирани и добре документирани.