Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Kontrolery - centurionowie Imperium

Do bramy obozu przybywa posłaniec z żądaniem: GET /legion/5. Serwer je odebrał - ale kto ma je obsłużyć? Aplikacja ma kilkadziesiąt metod i żadna nie wie, że to właśnie do niej.

W legionie od rozdzielania rozkazów jest centurion. Stoi między posłańcem a żołnierzami: przyjmuje rozkaz, rozpoznaje, kogo dotyczy, przekazuje dalej i odsyła odpowiedź. Sam nie wykonuje pracy - on ją kieruje. W NestJS tym centurionem jest kontroler.

Deklaracja kontrolera

Kontroler to klasa z dekoratorem wskazującym, jakim odcinkiem tras się zajmuje:

1@Controller('legion')
2export class LegionController { }

Kolejność jest zawsze ta sama:

@Controller('legion')
, potem
export class
, na końcu nazwa klasy z ciałem.

Argument

'legion'
to prefiks trasy. Wszystkie metody tej klasy będą obsługiwać adresy zaczynające się od
/legion
- i nie musisz tego powtarzać przy każdej z nich. Kontroler bez argumentu,
@Controller()
, przejmuje trasy od korzenia.

Cztery czasowniki

Wewnątrz kontrolera każda metoda dostaje dekorator mówiący, na jakie żądanie odpowiada:

1@Controller('tributes')
2export class TributesController {
3  @Get()
4  findAll() { }
5
6  @Post()
7  create() { }
8
9  @Put(':id')
10  update() { }
11
12  @Delete(':id')
13  remove() { }
14}

Cztery dekoratory odpowiadają czterem czynnościom na zasobie.

@Get()
pobiera,
@Post()
tworzy nowy zasób,
@Put()
aktualizuje istniejący,
@Delete()
usuwa.

Rozróżnienie

@Post
od
@Put
bywa mylące, więc zapamiętaj je po skutku powtórzenia: wysłanie tego samego
@Post
dwa razy utworzy dwa zasoby, a tego samego
@Put
- zostawi jeden, po prostu zapisany dwukrotnie.

Argument dekoratora dokłada kawałek trasy.

@Put(':id')
w kontrolerze z prefiksem
tributes
obsłuży adres
/tributes/42
. Dwukropek oznacza parametr - miejsce, w które wpada dowolna wartość.

Trzy miejsca, z których biorą się dane

Skoro trasa może zawierać parametr, trzeba go jakoś odczytać. Dane przychodzą w żądaniu trzema drogami i każda ma swój dekorator:

1@Get(':id')
2findOne(@Param('id') id: string) { }
3
4@Get('search')
5search(@Query('province') province: string) { }
6
7@Post()
8create(@Body() dto: CreateTributeDto) { }

@Param('id')
wyciąga wartość ze ścieżki adresu - to on odczyta
5
z
/legion/5
.
@Query('province')
bierze parametr zapytania, czyli to, co stoi po znaku zapytania:
?province=rome
.
@Body()
sięga po ciało żądania, wysyłane przy
@Post
i
@Put
.

Te trzy pokrywają wszystko, czego zwykle potrzebujesz. Uwaga na dwie nazwy, które brzmią prawdopodobnie:

@Path()
w NestJS nie istnieje - do ścieżki służy
@Param
.
@Header()
istnieje, ale robi coś innego
: ustawia nagłówek odpowiedzi. Do czytania nagłówków żądania służy
@Headers()
, w liczbie mnogiej.

Metoda kontrolera od środka

Złóżmy to w całość. Kolejność elementów metody jest niezmienna:

1@Get()
2getAllLegionaries() {
3  return this.service.findAll();
4}

Najpierw dekorator

@Get()
, potem nazwa metody, dalej ciało w nawiasach klamrowych, a w nim
return this.service.findAll()
.

I tu widać, czym kontroler naprawdę jest. Ta metoda nie zawiera logiki - przyjmuje żądanie i przekazuje je serwisowi. Centurion nie kuje mieczy; wie, do którego kowala posłać.

To jest reguła, którą polecam trzymać od pierwszego dnia, @name: w kontrolerze nie ma zapytań do bazy, obliczeń ani reguł biznesowych. Gdy metoda kontrolera rośnie ponad kilka linii, to znak, że robi coś, co należy do serwisu. Zysk jest praktyczny - tę samą logikę wywołasz potem z zadania wsadowego albo konsumenta kolejki, gdzie żadnego HTTP nie ma.

Zwróć uwagę, że nie budujemy odpowiedzi ręcznie. Zwrócona wartość zostanie zamieniona na JSON, a kod odpowiedzi NestJS dobierze sam: 200 dla większości metod, 201 dla

@Post
, bo coś powstało.

Podsumowanie

Centurion stoi u bramy i wie, komu przekazać rozkaz:

  • kontroler przyjmuje żądanie i kieruje je dalej - sam nie wykonuje pracy,
  • deklaracja w kolejności:
    @Controller('prefiks')
    ,
    export class
    , nazwa klasy,
  • argument
    @Controller
    to prefiks trasy wspólny dla wszystkich metod klasy,
  • cztery czasowniki:
    @Get()
    pobiera,
    @Post()
    tworzy nowy zasób
    ,
    @Put()
    aktualizuje,
    @Delete()
    usuwa,
  • powtórzony
    @Post
    tworzy dwa zasoby, powtórzony
    @Put
    - jeden,
  • dwukropek w trasie oznacza parametr:
    @Put(':id')
    obsłuży
    /tributes/42
    ,
  • @Param('id')
    czyta ze ścieżki
    ,
    @Query('nazwa')
    czyta parametr zapytania
    po znaku
    ?
    ,
    @Body()
    czyta ciało żądania,
  • @Path()
    nie istnieje
    ;
    @Header()
    ustawia nagłówek odpowiedzi, a nagłówki żądania czyta
    @Headers()
    ,
  • kolejność w metodzie: dekorator, nazwa, ciało,
    return this.service.findAll()
    ,
  • w kontrolerze nie ma logiki biznesowej - dzięki temu wywołasz ją też poza HTTP,
  • kod odpowiedzi dobiera NestJS: 200, a dla
    @Post
    - 201.

W następnej lekcji poznasz tego, komu centurion przekazuje rozkazy - serwisy, czyli miejsce, w którym mieszka prawdziwa logika. A na razie zapamiętaj: kontroler rozpoznaje żądanie i wskazuje wykonawcę - a każde dane, których potrzebuje, przychodzą jedną z trzech dróg: ścieżką, zapytaniem albo ciałem.

Przejdź do CodeWorlds