Bootcamp i backendutvikling med Node.js · leksjon

Interceptorer, tidsfrister og metadata

Legg til tverrgående autentisering og logging med interceptorer, samtidig som De håndhever tidsfrister og sender metadata.

Leksjon 3 av 413 trinn

Interceptorer, tidsfrister og metadata er en gratis leksjon i Bootcamp i backendutvikling med Node.js på CoddyKit. Dette er leksjon 3 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Bootcamp i backendutvikling med Node.js, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Bootcamp i backendutvikling med Node.js inneholder totalt 4 leksjoner.

Felles hensyn i gRPC

Når gRPC-tjenesten vokser, gjentar du den samme logikken i hver handler: autentisering, logging, tidsmåling og utforming av feil. Det er skjørt å kopiere dette inn i hver metode.

gRPC gir deg tre verktøy for å håndtere dette på en ryddig måte:

  • Interceptorer — mellomvare som omslutter hvert kall, på klient- eller serversiden.
  • Metadata — nøkkel/verdi-hoder som følger forespørselen, perfekt for autentiseringstokener og forespørsels-ID-er.
  • Tidsfrister — et absolutt tidspunkt som et kall må være ferdig innen, og som videreføres mellom tjenester.

I denne leksjonen kobler vi alle tre sammen med @grpc/grpc-js i Node.js.

Hva metadata faktisk er

Metadata er en multimengde av strengnøkler med verdier som sendes med et gRPC-kall, omtrent som HTTP-hoder. Nøkler skiller ikke mellom store og små bokstaver. Verdiene er vanligvis ASCII-strenger; nøkler som slutter på -bin, inneholder binære Buffer-verdier.

På klienten legger du ved metadata; på serveren leser du den fra call-objektet. Bruk den til ting som ikke er en del av nyttelasten for virksomhetslogikken — autentiseringstokener, sporings-ID-er og lokalitet.

const grpc = require('@grpc/grpc-js');

// Build metadata on the client
const md = new grpc.Metadata();
md.set('authorization', 'Bearer abc123');
md.set('x-request-id', 'req-42');

// Reading is case-insensitive
console.log(md.get('Authorization')); // [ 'Bearer abc123' ]
console.log(md.get('x-request-id')); // [ 'req-42' ]

Sende metadata med et unary-kall

Alle genererte klientmetoder godtar et valgfritt Metadata-argument før callbacken eller alternativene. Tokenet du angir her, kommer frem til serveren før handleren din kjøres.

Hold autentiseringshensyn utenfor protobuf-meldingene — legg tokenet i metadata, slik at den samme autentiseringslogikken fungerer for alle RPC-kall.

const grpc = require('@grpc/grpc-js');

function callWithAuth(client, token) {
  const md = new grpc.Metadata();
  md.set('authorization', 'Bearer ' + token);

  client.GetUser({ id: '7' }, md, (err, res) => {
    if (err) return console.error('RPC failed:', err.message);
    console.log('User:', res);
  });
}

Lese metadata på serveren

På serveren eksponerer call-objektet metadata via call.metadata.get(key), som returnerer en array (en nøkkel kan forekomme flere ganger). Ved autentisering leser du authorization-headeren og validerer den før du gjør arbeid.

Hvis valideringen mislykkes, returnerer du en feil med grpc.status.UNAUTHENTICATED, slik at klientene kan reagere riktig.

const grpc = require('@grpc/grpc-js');

function getUser(call, callback) {
  const auth = call.metadata.get('authorization')[0];
  if (!auth || !auth.startsWith('Bearer ')) {
    return callback({
      code: grpc.status.UNAUTHENTICATED,
      message: 'Missing or invalid token',
    });
  }
  const token = auth.slice('Bearer '.length);
  // ... verify token, then respond
  callback(null, { id: call.request.id, name: 'Ada' });
}

Serverinterceptorer: Ideen

Det er gjentakende arbeid å lese og validere tokener inne i hver handler. En serverinterceptor lar deg kjøre logikk én gang for alle RPC-kall.

I @grpc/grpc-js tilbys interceptorer på serversiden gjennom alternativet interceptors i server.addService (og via ServerInterceptingCall i nyere versjoner). Det klassiske, bredt støttede mønsteret er en klientinterceptor; på serveren omslutter mange team handlerne med en liten høyereordensfunksjon for autentisering og logging.

La oss begynne med dette mønsteret for innpakking av handlere — det er portabelt og enkelt å teste.

En autentiseringswrapper for handlere

En høyereordensfunksjon tar imot en handler og returnerer en ny handler som først kontrollerer autentisering og deretter delegerer. Dette er en ryddig måte å legge til et felles hensyn på, uten rammeverk.

Wrapperen avslutter med UNAUTHENTICATED når tokenet er ugyldig, og kaller ellers den opprinnelige handleren.

const grpc = require('@grpc/grpc-js');

function withAuth(handler) {
  return (call, callback) => {
    const auth = call.metadata.get('authorization')[0];
    if (auth !== 'Bearer good-token') {
      return callback({
        code: grpc.status.UNAUTHENTICATED,
        message: 'Unauthorized',
      });
    }
    return handler(call, callback);
  };
}

// Usage when registering the service:
// server.addService(svc, { GetUser: withAuth(getUser) });

En loggingswrapper du kan sette sammen

Det samme mønsteret fungerer for logging og tidsmåling. Siden hver wrapper tar imot en handler og returnerer en handler, kan du sette dem sammen: withLogging(withAuth(getUser)).

Her registrerer loggeren metodeetiketten, ventetiden og forespørsels-ID-en fra metadata. Legg merke til at dette er et komplett, frittstående program som du kan kjøre for å se sammensetningen i praksis med et falskt call-objekt.

function withLogging(name, handler) {
  return (call, callback) => {
    const start = Date.now();
    const reqId = call.metadata.reqId || 'none';
    handler(call, (err, res) => {
      const ms = Date.now() - start;
      console.log(`[${name}] req=${reqId} ${err ? 'ERR' : 'OK'} ${ms}ms`);
      callback(err, res);
    });
  };
}

function getUser(call, callback) {
  callback(null, { id: call.request.id, name: 'Ada' });
}

const handler = withLogging('GetUser', getUser);
handler(
  { request: { id: '7' }, metadata: { reqId: 'req-42' } },
  (err, res) => console.log('Response:', res)
);

Klientinterceptorer

Det offisielle utvidelsespunktet på klienten er interceptoren: en funksjon som mottar options og en nextCall, og returnerer en InterceptingCall. Du overstyrer livssyklusmetoder som start for å legge inn metadata, eller onReceiveStatus for å observere resultater.

Nedenfor får alle utgående kall automatisk en authorization-header — den som utfører kallet, trenger aldri å huske den.

const grpc = require('@grpc/grpc-js');

function authInterceptor(token) {
  return (options, nextCall) =>
    new grpc.InterceptingCall(nextCall(options), {
      start(metadata, listener, next) {
        metadata.set('authorization', 'Bearer ' + token);
        next(metadata, listener);
      },
    });
}

// const client = new UserService(addr, creds, {
//   interceptors: [authInterceptor('abc123')],
// });

Forstå tidsfrister

En tidsfrist er et absolutt tidspunkt som et kall må være ferdig innen. Det er IKKE en tidsavbruddsgrense per hopp — det er et tidspunkt på veggklokken som videreføres nedstrøms, slik at en kjede av tjenester deler ett samlet budsjett.

Angi den i kall-options som deadline: enten som en Date eller som millisekunder siden epoken. Det idiomatiske mønsteret er «nå pluss N ms».

  • Hvis tidsfristen utløper, mislykkes kallet med DEADLINE_EXCEEDED (status 4).
  • Servere kan kontrollere den gjenværende tiden og avslutte arbeidet tidlig.
const grpc = require('@grpc/grpc-js');

function callWithDeadline(client) {
  const deadline = new Date(Date.now() + 2000); // 2s budget
  client.GetUser({ id: '7' }, { deadline }, (err, res) => {
    if (err && err.code === grpc.status.DEADLINE_EXCEEDED) {
      return console.error('Timed out');
    }
    if (err) return console.error(err.message);
    console.log(res);
  });
}

Tidsfrister på serversiden

Serveren mottar tidsfristen som call.getDeadline(). Før du starter kostbart arbeid, beregner du det gjenværende budsjettet og avbryter tidlig hvis det allerede er brukt opp — slik unngår du å sløse CPU på et kall klienten har gitt opp.

Når du sender forespørsler videre til nedstrøms tjenester, må du sende med den SAMME tidsfristen, slik at hele treet respekterer ett budsjett.

const grpc = require('@grpc/grpc-js');

function slowHandler(call, callback) {
  const deadline = call.getDeadline(); // ms or Date
  const remaining = Number(deadline) - Date.now();
  if (remaining <= 0) {
    return callback({ code: grpc.status.DEADLINE_EXCEEDED, message: 'No time left' });
  }
  // Forward the same deadline downstream:
  // downstream.Fetch(req, { deadline }, cb);
  callback(null, { ok: true, budgetMs: remaining });
}

Slik henger det sammen: en pipeline med budsjett

Her er tankemodellen for én forespørsel: Klienten setter en tidsfrist og autentiseringsmetadata. En klientinterceptor legger inn tokenet; kallet inneholder en forespørsels-ID. På serveren validerer en autentiseringswrapper tokenet, en loggingswrapper måler tiden, og handlerne kontrollerer den gjenværende tidsfristen før de sender forespørsler videre — og viderefører både metadata og tidsfristen nedstrøms.

Denne frittstående simuleringen viser hele kjeden av wrappere og en kontroll av tidsfristen uten noen server, slik at du kan kjøre den direkte.

function withAuth(h) {
  return (call, cb) =>
    call.metadata.authorization === 'Bearer good'
      ? h(call, cb)
      : cb({ code: 16, message: 'UNAUTHENTICATED' });
}
function withDeadline(h) {
  return (call, cb) =>
    call.deadline - Date.now() <= 0
      ? cb({ code: 4, message: 'DEADLINE_EXCEEDED' })
      : h(call, cb);
}
function getUser(call, cb) {
  cb(null, { id: call.request.id, name: 'Ada' });
}

const pipeline = withAuth(withDeadline(getUser));
const call = {
  request: { id: '7' },
  metadata: { authorization: 'Bearer good' },
  deadline: Date.now() + 1000,
};
pipeline(call, (err, res) =>
  console.log(err ? 'Error ' + err.code : 'OK', res || '')
);

Kort kontroll

En klient setter deadline = Date.now() + 3000 og kaller tjeneste A, som deretter kaller tjeneste B. Hva er riktig oppførsel for tidsfristen?

Oppsummering

Du har nå verktøyene du trenger for gRPC-kall i produksjonskvalitet:

  • Metadata inneholder data utenfor nyttelasten, som authorization og x-request-id; les den på serversiden med call.metadata.get(key) (returnerer en array, og nøkler skiller ikke mellom store og små bokstaver).
  • Interceptorer sentraliserer felles logikk: klientinterceptorer bruker InterceptingCall til å legge inn metadata i start; på serveren legger sammensettbare handlerwrappere til autentisering og logging én gang.
  • Tidsfrister er absolutte tidspunkter som angis via kall-options.deadline og videreføres uendret nedstrøms, slik at en hel kjede deler ett budsjett; overskridelse gir DEADLINE_EXCEEDED (status 4).

Kombiner dem: legg inn autentisering og forespørsels-ID via en interceptor, pakk inn handlere for autentisering og logging, og send alltid tidsfristen videre når du fordeler arbeidet til andre tjenester.

Gratis å komme i gang

Lær deg JavaScript med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
22
Leksjoner
92

Ofte stilte spørsmål

Er leksjonen «Interceptorer, tidsfrister og metadata» gratis?

Ja – hele teksten i «Interceptorer, tidsfrister og metadata» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Bootcamp i backendutvikling med Node.js-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Bootcamp i backendutvikling med Node.js inneholder totalt 4 leksjoner.

Hva lærer jeg i «Interceptorer, tidsfrister og metadata»?

Legg til tverrgående autentisering og logging med interceptorer, samtidig som De håndhever tidsfrister og sender metadata. Du øver på Bootcamp i backendutvikling med Node.js med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Bootcamp i backendutvikling med Node.js?

Ingen tidligere erfaring er nødvendig. Bootcamp i backendutvikling med Node.js på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Interceptorer, tidsfrister og metadata»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Bootcamp i backendutvikling med Node.js-leksjonen?

Ja. Alle Bootcamp i backendutvikling med Node.js-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Definering av tjenester og meldinger med Protobuf IDL
  2. Unary-, server-, klient- og toveisstrømmende RPC-er
  3. Interceptorer, tidsfrister og metadata
  4. Proto-evolusjon og bakoverkompatibilitet
← Tilbake til Bootcamp i backendutvikling med Node.js