Serwer proxy Nginx według metody żądania

17

Czy to możliwe / jak mogę skonfigurować blok lokalizacji Nginx do proxy do różnych backendów w zależności od metody żądania (np. GET / POST)?

Powodem jest to, że obecnie obsługuję 2 metody pod dwoma różnymi adresami URL (jeden za pośrednictwem serwera proxy HTTP, a drugi za pośrednictwem fcgi) i staram się, aby było to bardziej „REST”, więc idealnie byłoby, gdyby GETting zasób zwrócił listę , podczas gdy test POST do tego samego zasobu powinien zostać dodany do listy.

Brenton Alker
źródło

Odpowiedzi:

27

Nie używam tej konfiguracji, ale na podstawie przykładów tutaj :

location /service  {
  if ($request_method = POST ) {
    fastcgi_pass 127.0.0.1:1234;
  }

  if ($request_method = GET ) {
     alias /path/to/files;
  }
}

Jeśli piszesz własną aplikację, możesz również rozważyć sprawdzenie w niej GET / POST i wysłanie nagłówków X-Accel-Redirect w celu przekazania transportu plików do nginx.

Jason
źródło
Blok GET jest w moim przypadku proxy_pass, ale poza tym to działa. W tej chwili nie używam drugiego bloku if, wydaje się , że nginx zatrzymuje „przetwarzanie” po osiągnięciu dyrektywy fastcgi_pass (tj. Nie spada i nie uruchamia również proxy proxy), ponieważ chcę, aby cokolwiek innego niż POST powróciło do pełnomocnika.
Brenton Alker,
2
Zauważ, że ifgeneralnie odradza to dokumentacja Nginx: nginx.com/resources/wiki/start/topics/depth/ifisevil
vog
1
Więc jaka jest alternatywa?
WM
1
@WM Zobacz moją odpowiedź: serverfault.com/a/823053/175421
vog
@vog, ciekawe. Całkiem sprytny sposób na zrobienie tego. Dzięki za udostępnienie.
WM
23

Chociaż można to osiągnąć za pomocą dokumentacji Nginx , ifogólnie nie jest to zalecane , ponieważ ifnie działa dobrze z innymi dyrektywami. Załóżmy na przykład, że GET powinien być otwarty dla wszystkich, podczas gdy POST jest przeznaczony tylko dla uwierzytelnionych użytkowników korzystających z HTTP Basic Auth. Wymagałoby ifto połączenia z tym auth_basic, co nie działa poprawnie.

Oto alternatywa, która działa bez if. Sztuką jest użycie „GET” i „POST” jako części nadrzędnych nazw, więc można je rozwiązać przez podstawienie zmiennych:

http {
  upstream other_GET {
    server ...;
  }
  upstream other_POST {
    server ...;
  }
  server {
    location /service {
      proxy_pass http://other_$request_method;
    }
  }
}

Aby połączyć to z HTTP Basic Auth dla wszystkiego oprócz GET, po prostu dodaj limit_exceptblok:

  ...
    location /service {
      proxy_pass http://other_$request_method;
      limit_except GET {
        auth_basic ...;
      }
    }
  ...
vog
źródło
Problem z tym podejściem polega na tym, że teraz wrócimy z 502 gateway errorpowodu no resolver defined to resolve other_HEAD(lub cokolwiek, czego brakuje w górnej części łańcucha). Zwrot czegoś takiego będzie bardziej semantyczny 405 method not allowed. Czy istnieje sposób na osiągnięcie tego?
James
1
@James: To może być sformułowane jako nowe pytanie odnoszące się do tego. Nie mam odpowiedzi na ten szczegół, ale może inni też.
vog
0

To właśnie zrobiłem, aby wszystko działało dla mnie

add_header Allow "GET, POST, HEAD" always;
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
    proxy_pass http://back-end;
}
Mansur Ali
źródło
Motyka dokładnie to przełącza między dwoma punktami końcowymi na podstawie metody żądania?
Podstawowy
0

Niewielka zmiana w odpowiedzi voga, aby uwzględnić domyślny moduł obsługi dla innych metod, takich jak OPTIONS, PUT itp.

    upstream webdav_default {
            server example.com;
    }
    upstream webdav_upload {
            server example.com:8081;
    }
    upstream webdav_download {
            server example.com:8082;
    }
    server {
            map upstream_location $request_method {
                    GET     webdav_download;
                    HEAD    webdav_download;
                    PUT     webdav_upload;
                    LOCK    webdav_upload;
                    default webdav_default;
            }
            location / {
                    proxy_pass https://$upstream_location;
            }
    }
timmmmmy
źródło
0

Nie mogłem uzyskać odpowiedzi z @timmmmmy do pracy, ale wskazało mi to dokumentację mapy i to zadziałało dla mnie:

map $request_method $upstream_location {
   PUT     example.com:8081;
   POST    example.com:8081;
   PATCH   example.com:8081;
   default example.com:8082;
}
server {
   location / {
      proxy_pass https://$upstream_location;
   }
}
rik harris
źródło