En omfattende guide til å forstå og implementere TypeScript middleware i Express.js-applikasjoner. Utforsk avanserte type-mønstre for robust og vedlikeholdbar kode.
TypeScript Middleware: Mestring av Express Middleware Type Patterns
Express.js, et minimalistisk og fleksibelt Node.js webapplikasjonsrammeverk, lar utviklere bygge robuste og skalerbare APIer og webapplikasjoner. TypeScript forbedrer Express ved å legge til statisk typing, forbedre kodevedlikeholdbarheten og fange feil tidlig. Middleware-funksjoner er en hjørnestein i Express, som gjør at du kan avskjære og behandle forespørsler før de når rutingshåndtererne dine. Denne artikkelen utforsker avanserte TypeScript type-mønstre for å definere og bruke Express middleware, noe som forbedrer typesikkerheten og kodeklarheten.
Forstå Express Middleware
Middleware-funksjoner er funksjoner som har tilgang til forespørselsobjektet (req), respons-objektet (res) og den neste middleware-funksjonen i applikasjonens forespørsel-respons syklus. Middleware-funksjoner kan utføre følgende oppgaver:
- Utfør kode.
- Gjør endringer i forespørsel- og respons-objektene.
- Avslutt forespørsel-respons syklusen.
- Kall den neste middleware-funksjonen i stakken.
Middleware-funksjoner utføres sekvensielt når de legges til i Express-applikasjonen. Vanlige bruksområder for middleware inkluderer:
- Logging av forespørsler.
- Autentisering av brukere.
- Autorisering av tilgang til ressurser.
- Validering av forespørselsdata.
- Håndtering av feil.
Grunnleggende TypeScript Middleware
I en grunnleggende TypeScript Express-applikasjon kan en middleware-funksjon se slik ut:
import { Request, Response, NextFunction } from 'express';
function loggerMiddleware(req: Request, res: Response, next: NextFunction) {
console.log(`Forespørsel: ${req.method} ${req.url}`);
next();
}
export default loggerMiddleware;
Denne enkle middleware logger forespørselsmetoden og URL-en til konsollen. La oss bryte ned typeannotasjonene:
Request: Representerer Express-forespørselsobjektet.Response: Representerer Express-respons-objektet.NextFunction: En funksjon som, når den påkalles, utfører neste middleware i stakken.
Du kan bruke denne middleware i Express-applikasjonen din slik:
import express from 'express';
import loggerMiddleware from './middleware/loggerMiddleware';
const app = express();
const port = 3000;
app.use(loggerMiddleware);
app.get('/', (req, res) => {
res.send('Hallo, verden!');
});
app.listen(port, () => {
console.log(`Server lytter på port ${port}`);
});
Avanserte Type Patterns for Middleware
Mens det grunnleggende middleware-eksemplet er funksjonelt, mangler det fleksibilitet og typesikkerhet for mer komplekse scenarier. La oss utforske avanserte type-mønstre som forbedrer middleware-utvikling med TypeScript.
1. Egendefinerte Forespørsel/Respons Typer
Ofte trenger du å utvide Request- eller Response-objektene med egendefinerte egenskaper. For eksempel, etter autentisering, vil du kanskje legge til en user-egenskap i Request-objektet. TypeScript lar deg utvide eksisterende typer ved hjelp av deklarasjonssammenslåing.
// src/types/express/index.d.ts
import { Request as ExpressRequest } from 'express';
declare global {
namespace Express {
interface Request {
user?: {
id: string;
email: string;
// ... andre brukeregenskaper
};
}
}
}
export {}; // Dette er nødvendig for å gjøre filen til en modul
I dette eksemplet utvider vi Express.Request-grensesnittet for å inkludere en valgfri user-egenskap. Nå, i din autentiseringsmiddleware, kan du fylle ut denne egenskapen:
import { Request, Response, NextFunction } from 'express';
function authenticationMiddleware(req: Request, res: Response, next: NextFunction) {
// Simulere autentiseringslogikk
const userId = req.headers['x-user-id'] as string; // Eller hent fra en token, etc.
if (userId) {
// I en ekte applikasjon, vil du hente brukeren fra en database
req.user = {
id: userId,
email: `user${userId}@example.com`
};
next();
} else {
res.status(401).send('Uautorisert');
}
}
export default authenticationMiddleware;
Og i rutingshåndtererne dine kan du trygt få tilgang til req.user-egenskapen:
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(`Hallo, ${req.user.email}! Din bruker-ID er ${req.user.id}`);
} else {
// Dette skal aldri skje hvis middleware fungerer riktig
res.status(500).send('Intern Serverfeil');
}
});
app.listen(port, () => {
console.log(`Server lytter på port ${port}`);
});
2. Middleware Fabrikker
Middleware-fabrikker er funksjoner som returnerer middleware-funksjoner. Dette mønsteret er nyttig når du trenger å konfigurere middleware med spesifikke alternativer eller avhengigheter. For eksempel, vurder en logger-middleware som logger meldinger til en bestemt fil:
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()}] Forespørsel: ${req.method} ${req.url}\n`;
fs.appendFile(logFilePath, logMessage, (err) => {
if (err) {
console.error('Feil ved skriving til loggfil:', err);
}
next();
});
};
}
export default createLoggingMiddleware;
Du kan bruke denne middleware-fabrikken slik:
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('Hallo, verden!');
});
app.listen(port, () => {
console.log(`Server lytter på port ${port}`);
});
3. Asynkron Middleware
Middleware-funksjoner trenger ofte å utføre asynkrone operasjoner, som databasespørringer eller API-kall. For å håndtere asynkrone operasjoner riktig, må du sørge for at next-funksjonen blir kalt etter at den asynkrone operasjonen er fullført. Du kan oppnå dette ved å bruke async/await eller Promises.
import { Request, Response, NextFunction } from 'express';
async function asyncMiddleware(req: Request, res: Response, next: NextFunction) {
try {
// Simulere en asynkron operasjon
await new Promise(resolve => setTimeout(resolve, 100));
console.log('Asynkron operasjon fullført');
next();
} catch (error) {
next(error); // Send feilen til feilhåndteringsmiddlewaren
}
}
export default asyncMiddleware;
Viktig: Husk å håndtere feil i din asynkrone middleware og sende dem til feilhåndteringsmiddlewaren ved å bruke next(error). Dette sikrer at feil håndteres og logges riktig.
4. Feilhåndteringsmiddleware
Feilhåndteringsmiddleware er en spesiell type middleware som håndterer feil som oppstår under forespørsel-respons syklusen. Feilhåndteringsmiddleware-funksjoner har fire argumenter: err, req, res og 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('Noe gikk galt!');
}
export default errorHandler;
Du må registrere feilhåndteringsmiddleware etter alle andre middleware og rutingshåndterere. Express identifiserer feilhåndteringsmiddleware ved tilstedeværelsen av de fire argumentene.
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('Simulert feil!'); // Simulere en feil
});
app.use(errorHandler); // Feilhåndteringsmiddleware MÅ registreres sist
app.listen(port, () => {
console.log(`Server lytter på port ${port}`);
});
5. Forespørselsvalideringsmiddleware
Forespørselsvalidering er et viktig aspekt ved å bygge sikre og pålitelige APIer. Middleware kan brukes til å validere innkommende forespørselsdata og sikre at den oppfyller visse kriterier før den når rutingshåndtererne dine. Biblioteker som joi eller express-validator kan brukes til forespørselsvalidering.
Her er et eksempel ved hjelp av 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;
Denne middleware validerer email- og password-feltene i forespørselsorganet. Hvis valideringen mislykkes, returnerer den et 400 Bad Request-svar med en rekke feilmeldinger. Du kan bruke denne middleware i rutingshåndtererne dine slik:
import express from 'express';
import validateCreateUserRequest from './middleware/validateCreateUserRequest';
const app = express();
const port = 3000;
app.post('/users', validateCreateUserRequest, (req, res) => {
// Hvis valideringen lykkes, opprett brukeren
res.send('Bruker opprettet!');
});
app.listen(port, () => {
console.log(`Server lytter på port ${port}`);
});
6. Avhengighetsinjeksjon for Middleware
Når middleware-funksjonene dine er avhengige av eksterne tjenester eller konfigurasjoner, kan avhengighetsinjeksjon bidra til å forbedre testbarhet og vedlikeholdbarhet. Du kan bruke en avhengighetsinjeksjonsbeholder som tsyringe eller bare sende avhengigheter som argumenter til middleware-fabrikkene dine.
Her er et eksempel ved hjelp av en middleware-fabrikk med avhengighetsinjeksjon:
// src/services/UserService.ts
export class UserService {
async createUser(email: string, password: string): Promise {
// I en ekte applikasjon, vil du lagre brukeren i en database
console.log(`Oppretter bruker med e-post: ${email} og passord: ${password}`);
await new Promise(resolve => setTimeout(resolve, 500)); // Simulere en databaseoperasjon
}
}
// 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('Bruker opprettet!');
} 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()); // Tolk JSON-forespørselsorganer
const userService = new UserService();
const createUserMiddleware = createCreateUserMiddleware(userService);
app.post('/users', createUserMiddleware);
app.use(errorHandler);
app.listen(port, () => {
console.log(`Server lytter på port ${port}`);
});
Beste Praksis for TypeScript Middleware
- Hold middleware-funksjoner små og fokuserte. Hver middleware-funksjon skal ha ett enkelt ansvar.
- Bruk beskrivende navn for dine middleware-funksjoner. Navnet skal tydelig indikere hva middleware gjør.
- Håndter feil ordentlig. Alltid fange feil og sende dem til feilhåndteringsmiddleware ved å bruke
next(error). - Bruk egendefinerte forespørsel/respons-typer for å forbedre typesikkerheten. Utvid
Request- ogResponse-grensesnittene med egendefinerte egenskaper etter behov. - Bruk middleware-fabrikker til å konfigurere middleware med spesifikke alternativer.
- Dokumenter dine middleware-funksjoner. Forklar hva middleware gjør og hvordan den skal brukes.
- Test dine middleware-funksjoner grundig. Skriv enhetstester for å sikre at middleware-funksjonene dine fungerer riktig.
Konklusjon
TypeScript forbedrer utviklingen av Express middleware betydelig ved å legge til statisk typing, forbedre kodevedlikeholdbarheten og fange feil tidlig. Ved å mestre avanserte type-mønstre som egendefinerte forespørsel/respons-typer, middleware-fabrikker, asynkron middleware, feilhåndteringsmiddleware og forespørselsvalideringsmiddleware, kan du bygge robuste, skalerbare og typesikre Express-applikasjoner. Husk å følge beste praksis for å holde middleware-funksjonene dine små, fokuserte og godt dokumenterte.