Detaljan vodič za razumijevanje i implementaciju TypeScript middlewarea u Express.js aplikacijama. Istražite napredne obrasce tipova za robustan i održiv kod.
TypeScript Middleware: Ovladavanje obrascima tipova Express Middlewarea
Express.js, minimalistički i fleksibilan Node.js okvir za web aplikacije, omogućuje programerima izgradnju robusnih i skalabilnih API-ja i web aplikacija. TypeScript poboljšava Express dodavanjem statičkog tipiziranja, poboljšavajući održivost koda i rano hvatanje grešaka. Middleware funkcije su kamen temeljac Expressa, omogućujući vam presretanje i obradu zahtjeva prije nego što stignu do vaših rukovatelja ruta. Ovaj članak istražuje napredne obrasce tipova TypeScripta za definiranje i korištenje Express middlewarea, poboljšavajući sigurnost tipova i jasnoću koda.
Razumijevanje Express Middlewarea
Middleware funkcije su funkcije koje imaju pristup objektu zahtjeva (req), objektu odgovora (res) i sljedećoj middleware funkciji u ciklusu zahtjev-odgovor aplikacije. Middleware funkcije mogu obavljati sljedeće zadatke:
- Izvršavanje bilo kojeg koda.
- Izmjene na objektima zahtjeva i odgovora.
- Završetak ciklusa zahtjev-odgovor.
- Pozivanje sljedeće middleware funkcije u stogu.
Middleware funkcije se izvršavaju sekvencijalno kako se dodaju u Express aplikaciju. Uobičajeni slučajevi korištenja middlewarea uključuju:
- Evidentiranje zahtjeva.
- Autentifikacija korisnika.
- Autorizacija pristupa resursima.
- Validacija podataka zahtjeva.
- Rukovanje greškama.
Osnovni TypeScript Middleware
U osnovnoj TypeScript Express aplikaciji, middleware funkcija bi mogla izgledati ovako:
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;
Ovaj jednostavan middleware bilježi metodu zahtjeva i URL u konzolu. Razložimo anotacije tipova:
Request: Predstavlja Express objekt zahtjeva.Response: Predstavlja Express objekt odgovora.NextFunction: Funkcija koja, kada se pozove, izvršava sljedeći middleware u stogu.
Ovaj middleware možete koristiti u svojoj Express aplikaciji ovako:
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}`);
});
Napredni obrasci tipova za Middleware
Iako je osnovni primjer middlewarea funkcionalan, nedostaje mu fleksibilnost i sigurnost tipova za složenije scenarije. Istražimo napredne obrasce tipova koji poboljšavaju razvoj middlewarea s TypeScriptom.
1. Prilagođeni tipovi zahtjeva/odgovora
Često ćete trebati proširiti objekte Request ili Response s prilagođenim svojstvima. Na primjer, nakon autentifikacije, možda ćete htjeti dodati svojstvo user objektu Request. TypeScript vam omogućuje proširenje postojećih tipova korištenjem spajanja deklaracija.
// 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
U ovom primjeru proširujemo sučelje Express.Request kako bismo uključili opcionalno svojstvo user. Sada, u svom middlewareu za autentifikaciju, možete popuniti ovo svojstvo:
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;
A u svojim rukovateljima ruta, možete sigurno pristupiti svojstvu 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 tvornice
Middleware tvornice su funkcije koje vraćaju middleware funkcije. Ovaj obrazac je koristan kada trebate konfigurirati middleware s određenim opcijama ili ovisnostima. Na primjer, razmotrite middleware za bilježenje koji bilježi poruke u određenu datoteku:
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;
Ovu middleware tvornicu možete koristiti ovako:
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. Asinkroni Middleware
Middleware funkcije često trebaju izvoditi asinkrone operacije, poput upita baze podataka ili API poziva. Za ispravno rukovanje asinkronim operacijama, morate osigurati da se funkcija next pozove nakon što asinkrona operacija završi. To možete postići korištenjem async/await ili 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;
Važno: Ne zaboravite rukovati greškama unutar svog asinkronog middlewarea i proslijediti ih middlewareu za rukovanje greškama koristeći next(error). To osigurava da se greške pravilno obrađuju i bilježe.
4. Middleware za rukovanje greškama
Middleware za rukovanje greškama je posebna vrsta middlewarea koja obrađuje greške koje se javljaju tijekom ciklusa zahtjev-odgovor. Funkcije middlewarea za rukovanje greškama imaju četiri argumenta: err, req, res i 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 za rukovanje greškama morate registrirati nakon svih ostalih middlewarea i rukovatelja ruta. Express identificira middleware za rukovanje greškama prisutnošću četiri argumenta.
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 za validaciju zahtjeva
Validacija zahtjeva je ključan aspekt izgradnje sigurnih i pouzdanih API-ja. Middleware se može koristiti za validaciju dolaznih podataka zahtjeva i osiguranje da zadovoljavaju određene kriterije prije nego što stignu do vaših rukovatelja ruta. Knjižnice poput joi ili express-validator mogu se koristiti za validaciju zahtjeva.
Evo primjera korištenja 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;
Ovaj middleware validira polja email i password u tijelu zahtjeva. Ako validacija ne uspije, vraća odgovor 400 Bad Request s nizom poruka o greškama. Ovaj middleware možete koristiti u svojim rukovateljima ruta ovako:
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. Ubrizgavanje ovisnosti za Middleware
Kada vaše middleware funkcije ovise o vanjskim uslugama ili konfiguracijama, ubrizgavanje ovisnosti može pomoći u poboljšanju testabilnosti i održivosti. Možete koristiti spremnik za ubrizgavanje ovisnosti poput tsyringe ili jednostavno proslijediti ovisnosti kao argumente vašim middleware tvornicama.
Evo primjera korištenja middleware tvornice s ubrizgavanjem ovisnosti:
// 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}`);
});
Najbolje prakse za TypeScript Middleware
- Neka middleware funkcije budu male i fokusirane. Svaka middleware funkcija treba imati jednu odgovornost.
- Koristite opisna imena za svoje middleware funkcije. Naziv bi trebao jasno pokazivati što middleware radi.
- Pravilno rukujte greškama. Uvijek hvatajte greške i prosljeđujte ih middlewareu za rukovanje greškama koristeći
next(error). - Koristite prilagođene tipove zahtjeva/odgovora za poboljšanje sigurnosti tipova. Proširite sučelja
RequestiResponses prilagođenim svojstvima prema potrebi. - Koristite middleware tvornice za konfiguriranje middlewarea s određenim opcijama.
- Dokumentirajte svoje middleware funkcije. Objasnite što middleware radi i kako bi se trebao koristiti.
- Temeljito testirajte svoje middleware funkcije. Napišite jedinične testove kako biste osigurali da vaše middleware funkcije rade ispravno.
Zaključak
TypeScript značajno poboljšava razvoj Express middlewarea dodavanjem statičkog tipiziranja, poboljšavajući održivost koda i rano hvatanje grešaka. Ovladavanjem naprednim obrascima tipova kao što su prilagođeni tipovi zahtjeva/odgovora, middleware tvornice, asinkroni middleware, middleware za rukovanje greškama i middleware za validaciju zahtjeva, možete izgraditi robusne, skalabilne i tipski sigurne Express aplikacije. Ne zaboravite slijediti najbolje prakse kako bi vaše middleware funkcije bile male, fokusirane i dobro dokumentirane.