Wszystkie artykuły
Jak testować Event Webhooks SendGrid na localhost
SendGridemail webhookssignature verificationlocalhost

Jak testować Event Webhooks SendGrid na localhost

Aby przetestować SendGrid Event Webhook na hoście lokalnym, uruchom program obsługi lokalnie, ujawnij jego port za pomocą npx portpreview PORT, wprowadź wynikowy HTTPS jako adres URL posta SendGrid i weryfikuj każde żądanie za pomocą klucza publicznego Signed Event Webhook przed przetworzeniem jego zdarzeń.

Co wysyła SendGrid Event Webhook

Event Webhook raportuje, co dzieje się po zaakceptowaniu wiadomości przez SendGrid. Zdarzenia związane z dostarczalnością obejmują processed, delivered, deferred, bounce, oraz dropped. Zdarzenia związane z zaangażowaniem obejmują open, click, raporty o spamie i zmiany subskrypcji. Dokładne pola różnią się w zależności od typu zdarzenia, dlatego kieruj głównie na event i traktuj pola opcjonalne jako opcjonalne.

Treść żądania

A to JSON array, niekoniecznie jeden obiekt. SendGrid może umieścić kilka zdarzeń w jednym POST. Procedura obsługi, która zakłada req.body.event, po cichu przegapi partię. Oficjalna referencja Event Webhook dokumentuje nazwy i pola zdarzeń, w tym sg_event_id i sg_message_id.

Używaj zdarzeń jako faktów, a nie poleceń. Na przykład zdarzenie delivered może zaktualizować status wiadomości, podczas gdy click może dołączyć rekord zaangażowania. Unikaj sytuacji, w której procedura obsługi click nadpisuje późniejszy stan anulowania subskrypcji tylko dlatego, że żądania dotarły w niewłaściwej kolejności.

1. Create a local endpoint

This Express example deliberately applies a raw-body parser only to the SendGrid route. Signature verification depends on the exact bytes SendGrid signed; parsing and re-serializing JSON can change those bytes.

import express from 'express';
import { EventWebhook, EventWebhookHeader } from '@sendgrid/eventwebhook';

const app = express();
const verifier = new EventWebhook();
const publicKey = verifier.convertPublicKeyToECDSA(
  process.env.SENDGRID_WEBHOOK_PUBLIC_KEY,
);

app.post(
  '/webhooks/sendgrid',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    const signature = req.get(EventWebhookHeader.SIGNATURE());
    const timestamp = req.get(EventWebhookHeader.TIMESTAMP());

    if (!signature || !timestamp || !verifier.verifySignature(
      publicKey,
      req.body,
      signature,
      timestamp,
    )) {
      return res.status(403).send('invalid signature');
    }

    let events;
    try {
      events = JSON.parse(req.body.toString('utf8'));
    } catch {
      return res.status(400).send('invalid JSON');
    }
    if (!Array.isArray(events)) {
      return res.status(400).send('expected an event array');
    }

    await enqueueNewEvents(events);
    return res.sendStatus(204);
  },
);

app.use(express.json());
app.listen(3000);

Zainstaluj oficjalnego pomocnika za pomocą npm install @sendgrid/eventwebhook. Zamontuj globalny plik express.json() po tej trasie lub jawnie wyklucz tę ścieżkę. Ta sama zasada obowiązuje w Next.js, Fastify, NestJS, funkcjach bezserwerowych i bramkach API: zachowaj oryginalną treść jako bufor łańcuchowy lub bajtowy do czasu pomyślnej weryfikacji. Oficjalne repozytorium węzłów SendGrid ma pasujący przykład signed Event Webhook.

2. Give SendGrid an HTTPS URL

Keep the application running, then open a second terminal:

npx portpreview 3000

PortPreview prints a public HTTPS origin. If it is https://example.portpreview.dev, the complete Post URL is:

https://example.portpreview.dev/webhooks/sendgrid

The path must match the route exactly. Keep the tunnel process alive while testing. A tunnel forwards traffic; it does not replace your local server, so connection failures usually mean the app is stopped, listening on another port, or bound in a way the tunnel cannot reach.

3. Skonfiguruj Event Webhook w SendGrid

  1. W interfejsie użytkownika SendGrid, open Ustawienia > Ustawienia poczty.
  2. Uw ustawieniach webhooka, open Event Webhooks i wybierz Utwórz nowy webhook.
  3. EWłącz, dodaj adres URL PortPreview jako adres URL wpisu i wybierz tylko te akcje, których potrzebuje Twoja aplikacja.
  4. Under Funkcje zabezpieczeń, włącz Signed Event Webhook.
  5. Zapisz webhooka, ponownie open jego ustawienia, skopiuj wygenerowany publiczny klucz weryfikacyjny i zapisz go jako SENDGRID_WEBHOOK_PUBLIC_KEY.
  6. Użyj Test Your Integration, a następnie wyślij prawdziwą wiadomość, aby sprawdzić typy zdarzeń, które mają znaczenie.
W bieżącym przewodniku konfiguracji

SendGrid zauważono, że test wysyła przykładowe zdarzenia, a nie dane z rzeczywistej wysyłki poczty. Zapisz przed testowaniem weryfikacji signature: para kluczy jest generowana podczas zapisywania konfiguracji Signed Event Webhook.

Jak działa weryfikacja podpisanego webhooka SendGrid

Signed Event Webhook wykorzystuje ECDSA. SendGrid przechowuje klucz prywatny i wyświetla odpowiedni publiczny klucz weryfikacyjny. Każda dostawa obejmuje X-Twilio-Email-Event-Webhook-Signature i X-Twilio-Email-Event-Webhook-Timestamp. Weryfikacja obejmuje timestamp połączony z nieprzetworzonymi bajtami ładunku i skrótem SHA-256; signature jest zakodowany w Base64. Oficjalny pomocnik obsługuje konwersję klucza publicznego, dekodowanie signature, haszowanie i weryfikację ECDSA.

Jest to weryfikacja asymetryczna: wyświetlana wartość jest kluczem publicznym, a nie sekretem HMAC. Nie uruchamiaj ładunku poprzez JSON.stringify(), nie usuwaj białych znaków, nie dodawaj znaku nowej linii ani nie sprawdzaj pojedynczych elementów tablicy. Najpierw sprawdź pełne bajty żądania, a następnie przeanalizuj tablicę. Algorytm i nagłówki można znaleźć w dokumentacji SendGrid dotyczącej funkcji bezpieczeństwa.

A ważny signature stwierdza, że podpisane bajty pochodzą od posiadacza klucza prywatnego SendGrid i nie zostały zmienione. Nie czyni przetwarzania zdarzeń idempotentnym, nie zezwala na dowolne działania ani nie udowadnia, że ​​zdarzenie jest nowe. To osobne elementy sterujące.

Make batch processing idempotent

SendGrid retries failed POSTs, and networks can lose a successful response. Therefore, duplicate delivery is normal. Use each event's sg_event_id as the primary deduplication key, with a unique database constraint. If your product combines multiple SendGrid accounts or environments, namespace the key by provider and account or environment.

async function enqueueNewEvents(events) {
  for (const event of events) {
    await db.transaction(async (tx) => {
      const inserted = await tx.webhookReceipts.insertIfAbsent({
        provider: 'sendgrid',
        eventId: event.sg_event_id,
        receivedAt: new Date(),
      });
      if (!inserted) return;

      await tx.jobs.enqueue({
        type: 'process-sendgrid-event',
        payload: event,
      });
    });
  }
}

Wkładka paragonu i trwała kolejka powinny zostać zatwierdzone razem. Zwróć tylko 2xx po tym, jak partia będzie trwale accepted. Jeśli jedno zdarzenie zakończy się niepowodzeniem po zatwierdzeniu innych, odpowiedź inna niż 2xx może spowodować zwrócenie całego żądania; deduplikacja pozwala następnej próbie pominąć zdarzenia już accepted i bezpiecznie kontynuować. Nie używaj Set w pamięci w środowisku produkcyjnym, ponieważ ponowne uruchomienie go kasuje i wiele instancji nie udostępnia go. Szerszy przewodnik dotyczący ponawiania prób i idempotencji webhooka obejmuje trwałe wzorce.

Zrozumienie ponownych prób przed wybraniem kodów stanu

AZgodnie z dokumentacją SendGrid Event Webhook, odpowiedź 2xx oznacza, że POST zakończyło się sukcesem. Odpowiedź inna niż 2xx powoduje ponowne próby w rosnących odstępach czasu do 24 godzin po zdarzeniu; jest to okno kroczące dla każdego nowego zdarzenia zakończonego niepowodzeniem. To zachowanie oznacza, że trwała awaria signature może również generować powtarzające się próby, a zwrócenie 2xx dla zdarzenia, którego nigdy nie zapisałeś, powoduje jego utratę.

  • 2xx: cała partia została uwierzytelniona i trwale accepted, czyli każde zdarzenie jest już znane.
  • 4xx: nieprawidłowe lub nieuwierzytelnione dane wejściowe. Rejestruj tylko bezpieczną diagnostykę; spodziewaj się ogólnego zachowania ponawiania próby SendGrid innego niż 2xx.
  • 5xx: przejściowa awaria bazy danych, kolejki lub aplikacji, którą należy ponowić.

Staraj się, aby ścieżka żądania była krótka: weryfikuj, sprawdzaj poprawność zewnętrznego kształtu, atomowo deduplikuj i umieszczaj w kolejce, a następnie odpowiadaj. Wykonuj aktualizacje analizy poczty e-mail, synchronizację CRM i powiadomienia w plikach roboczych.

Rozwiązywanie problemów lokalnych SendGrid webhooków

signature jest zawsze nieprawidłowe

Najczęstszą przyczyną jest oprogramowanie pośrednie JSON zużywające treść przed weryfikacją. Potwierdź, że weryfikator otrzymał oryginalny Buffer, łącznie ze wszystkimi początkowymi i końcowymi białymi znakami. Następnie sprawdź, czy klucz publiczny należy do tej dokładnej konfiguracji Event Webhook i czy oba nagłówki Twilio docierają do aplikacji w niezmienionej postaci. Uruchom ponownie proces lokalny po zmianie jego środowiska.

Integracja testowa powiodła się, ale nie pojawiają się rzeczywiste zdarzenia

Sprawdź, czy element webhook jest włączony i czy wybrano żądane działania. Otwory wymagają śledzenia open, a click wymagają śledzenia click. Pamiętaj też, że żądanie testowe zawiera przykłady; użyj rzeczywistego wysyłania, aby sprawdzić pola i sekwencję przypominającą produkcję.

Punkt końcowy zwraca 404 lub 502

W przypadku 404 porównaj skonfigurowaną ścieżkę z /webhooks/sendgrid. W przypadku błędów bramy upewnij się, że aplikacja lokalna działa na tym samym porcie przekazanym do PortPreview. Jeśli nadejdą żądania, ale zwrócą 500, sprawdź lokalne dzienniki i tymczasowo zmniejsz procedurę obsługi do weryfikacji i trwałego przechwytywania.

Ezdarzenia są zduplikowane lub nie działają

To rzeczywistość systemu dostarczania, a nie dowód na to, że tunel powiela ruch. Deduplikuj za pomocą sg_event_id, tam, gdzie to możliwe, spraw, aby przejścia stanu były monotoniczne i przechowuj czas zdarzenia oddzielnie od czasu odbioru. Użyj lokalnego procesu debugowania webhooka, aby odizolować błędy transportu, uwierzytelniania i logiki biznesowej.

Lista kontrolna bezpieczeństwa do użytku lokalnego i produkcyjnego

  • Użyj HTTPS i weryfikuj każdy signature przed analizowaniem lub rejestrowaniem szczegółów zdarzenia.
  • Zachowaj publiczny klucz weryfikacyjny w konfiguracji, aby można go było bezproblemowo zaktualizować po zmianie klucza webhooka.
  • Akceptuj tylko POST, ograniczaj rozmiar żądania, sprawdź, czy analizowana wartość jest tablicą i zezwalaj tylko na obsługiwane nazwy zdarzeń.
  • Nie umieszczaj PII w kategoriach SendGrid ani unikalnych argumentach; Odniesienie SendGrid wyraźnie ostrzega, że te pola są przechowywane i nie są traktowane jako informacje umożliwiające identyfikację.
  • Nie ujawniaj sesji administratora, konsoli debugowania ani niepowiązanych tras lokalnych przez to samo tymczasowe źródło.
  • Nie rejestruj adresów odbiorców, ładunków, signature ani wartości środowiska, chyba że jest to konieczne i odpowiednio zredagowane.
  • Zastąp tymczasowy adres URL tunelu stabilnym produkcyjnym punktem końcowym HTTPS po przetestowaniu i wyłącz przestarzałe konfiguracje webhooka.

SendGrid może również używać OAuth 2.0 do zabezpieczenia Event Webhook, samodzielnie lub razem z signature. Jeśli Twoje wdrożenie wymaga kontroli cyklu życia nośnika token, postępuj zgodnie z oficjalnym przewodnikiem bezpieczeństwa, zamiast wymyślać wymianę token. Weryfikacja podpisu pozostaje cenna, ponieważ wiąże dokładne bajty timestamp i ładunku.

A test akceptacyjny gotowości do produkcji

  1. Wyślij podpisane żądanie testowe i potwierdź odpowiedź 2xx.
  2. Zmień jeden bajt ładunku i potwierdź 403 bez zapisu do bazy danych.
  3. Odtwórz ponownie identyczne ważne żądanie i potwierdź, że nie ma duplikatów zadań ani działań biznesowych.
  4. Wyślij obiekt JSON zamiast tablicy i potwierdź kontrolowane 400.
  5. Zatrzymaj na chwilę bazę danych, potwierdź 5xx, przywróć ją i sprawdź, czy ponowna próba to accepted raz.
  6. Wyślij prawdziwy e-mail i potwierdź, że wybrane zdarzenia dostawy i zaangażowania podążają tą samą ścieżką.

Po pomyślnym zakończeniu tych kontroli przenieś punkt końcowy do środowiska produkcyjnego bez zmiany logiki weryfikacji i idempotencji. W przypadku głębszych trybów awarii kryptograficznych zapoznaj się z przewodnikiem weryfikacji webhook signature.

Najczęściej zadawane pytania

Czy SendGrid może wysyłać Event Webhook do hosta lokalnego?
Nie bezpośrednio. Uruchom moduł obsługi lokalnie, uruchom `npx portpreview PORT` i skonfiguruj wygenerowany publiczny adres URL HTTPS oraz ścieżkę webhooka jako adres URL postu SendGrid.
Jak zweryfikować SendGrid podpisany Event Webhook?
Przeczytaj nagłówki X-Twilio-Email-Event-Webhook-Signature i X-Twilio-Email-Event-Webhook-Timestamp, zachowaj całą nieprzetworzoną treść żądania i zweryfikuj je za pomocą klucza publicznego, korzystając z oficjalnego pomocnika Event Webhook SendGrid.
Dlaczego weryfikacja webhooka SendGrid kończy się niepowodzeniem po analizie JSON?
ECDSA signature obejmuje timestamp plus dokładne surowe bajty ładunku. Analizowanie i ponowna serializacja JSON może zmienić białe znaki lub formatowanie, dlatego przed analizą JSON należy przeprowadzić weryfikację względem oryginalnego bufora lub łańcucha.
Czy ponowna próba SendGrid nie powiodła się? Event Webhooks?
Tak. SendGrid dokumentuje wydłużanie interwałów ponownych prób w przypadku odpowiedzi innych niż 2xx do 24 godzin po każdym zdarzeniu. Zwróć 2xx dopiero po uwierzytelnieniu partii i trwałym accepted oraz wykonaj deduplikację za pomocą sg_event_id.