Koa.js - middlewares

fsgeek.pl 4 lat temu

Aplikacje w Koa.js są budowane przy pomocy zestawów funkcji zwanych middlewares. Dzięki takiej architekturze jesteśmy w stanie wydzielić logikę do poszczególnych funkcji i korzystać z tych samych funkcji w różnych projektach. Dziś o tym jak tworzyć takie funkcje by było to możliwe, na co uważać przy tworzeniu oraz jak łączyć je z innymi.

Inne wpisy o Koa

  • Koa.js - pierwsze kroki
  • Koa.js - obsługa błędów

Middlewares

To co będziemy określać jako middleware to będzie dowolna funkcja, która została zarejstrowana przy pomocy app.use. Nasza funkcja powinna przyjmować przynajmniej jeden parametr- zwyczajowo nazywany ctx (od context), gdzie znajdują się najważniejsze dla nas informacje oraz ustawiamy odpowiedzi na zapytanie. Tak jak wspomniałem w poprzednim wpisie Koa mocno poszła w nowości ze świata JavaScriptu więc mamy tutaj możliwość korzystania z funkcji asynchronicznych oraz awaita.

Context

Najważniejszą rzeczą w każdym middleware jest zmienna ctx. Jak wspomniałem przechowuje dla nas wszytskie informacje oraz umożliwia tworzenie odpowiedzi na zapytanie. Znajdziemy tam takie pola:

  • req i res - są to natywne obiekty z Node'a - odpowiednio Request i Response
  • request - obiekt Request z Koa
  • response - obiekt Response z Koa
  • app - zawiera referencję do utworzonej aplikacji
  • cookies - pozwala na pobieranie i ustawianie cookies w aplikacji
  • throw - pozwala na rzucenie błędu
  • assert - pozwala na rzucenie błędu jeżeli przekazana wartość nie istnieje

Więcej o błędach i ich obsłudze będzie w innym poście.

Tworzenie własnych middlewares

Najprostszy sposób na stworzenie własnego middleware'a to stworzenie zwykłej funkcji oraz zarejestrowanie jej w aplikacji.

const simpleResponse = (ctx) => { ctx.body = "Hello World" ctx.status = 200; } app.use(simpleResponse2)

Kiedy tworzymy takie funkcje to chcemy je wykorzystywać w wielu miejscach - warto wtedy dać możliwość ustawiania opcji by można było dopasować funkcję do aktualnych potrzeb.

const simpleResponse = (status) => { const simpleResponse = (ctx) => { ctx.body = "Hello World" ctx.status = status; } return simpleResponse; } app.use(simpleResponse2(200))

Nawet jeżeli nasz middleware nie przyjmuje opcji to jest zalecane aby dalej utrzymywać taką strukturę.

Przechodzenie pomiędzy middlewares

Została ostatnia rzecz jeżeli chodzi o middlewares czyli przechodzenie pomiędzy różnymi stworzonymi przez nas funkcjami. Do tej pory mieliśmy zarejestrowany jeden middleware który od razu zwracał odpowiedź na zapytanie - jednak w prawdziwych aplikacjach będziemy ich mieć o wiele więcej. Przy komponowaniu naszych funkcji musimy pamiętać o kilku rzeczach.

Po pierwsze wszystkie funkcje są wywoływane w kolejności w jakiej je rejestrujemy - warto o tym pamiętać ponieważ kolejność czasami może mieć znaczenie.

Następna rzecz to przynajmniej jedna z funkcji powinna ustawiać ciało odpowiedzi - o ile więcej niż jedna funkcji to ustawia, to ciało odpowiedzi będzie równe ostatniemu ustawieniu.

Ostatnia rzecz o jakiej warto pamiętać to, iż jeżeli nasza funkcja nie ustawia odpowiedzi to musimy przejść do kolejnej funkcji. Robimy to przy pomocy drugiego parametru funkcji - next. Jest to funkcja która odsyła sterowanie do następnego middleware'a.

const simpleResponse = (status) => { const simpleResponse = (ctx) => { console.log(3); ctx.body = "Hello World" ctx.status = status; } return simpleResponse; } app.use((ctx, next)=>{ console.log(1) next(); }) app.use((ctx, next)=>{ console.log(2) next(); }) app.use(simpleResponse(200))

Po wykonaniu zapytania w konsoli pojawi się po kolei następująca sekwencja

1 2 3

Możemy również odwrócić kolejność wywoływania console.log() jeżeli najpierw wywołamy next(). W momencie gdy wykonamy wszystkie middlewary to wykonywanie funkcji wraca do poprzednich funkcji i jest wykonywany kod, który pozostał po funkcji next(). Najlepiej to będzie widać na przykładzie:

const simpleResponse = (status) => { const simpleResponse = (ctx) => { console.log(3); ctx.body = "Hello World" ctx.status = status; } return simpleResponse; } app.use((ctx, next)=>{ next(); console.log(1) }) app.use((ctx, next)=>{ next(); console.log(2) }) app.use(simpleResponse(200))

Teraz w konsoli będzie następująca sekwencja

3 2 1

Z jednej strony jest to interesująca opcja, ponieważ jesteśmy w stanie osiagnąć różne rzeczy np.: mierzyć statystyki (np.: czas) dla zapytań. Z drugiej strony debuggowanie może być problematyczne jeżeli o tym zapomnimy. To co dzisiaj omówiłem jest najważniejszym elementem Koa i wystarczająca do tworzenia aplikacji. Następnym razem opiszę jak reagować na błędy aplikacji.

Idź do oryginalnego materiału