Proxy Nginx per metodo di richiesta


17

È possibile / come posso configurare un blocco di posizione Nginx in modo che esegua il proxy su backend diversi a seconda del metodo di richiesta (es. GET / POST)?

Il motivo è che attualmente sto gestendo i 2 metodi in 2 URL diversi (uno tramite proxy HTTP e l'altro tramite fcgi) e sto cercando di renderlo più "REST", quindi idealmente mi piacerebbe OTTENERE la risorsa per restituire l'elenco , mentre POSTing alla stessa risorsa dovrebbe essere aggiunto all'elenco.

Risposte:


27

Non utilizzo questa configurazione, ma in base agli esempi qui :

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

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

Se stai scrivendo la tua applicazione, puoi anche considerare di controllare GET / POST e inviare le intestazioni X-Accel-Redirect per trasferire il trasporto dei file su nginx.


Il blocco GET è un proxy_pass nel mio caso, ma per il resto funziona. Al momento non sto usando il secondo blocco if, nginx sembra fermare l '"elaborazione" quando viene raggiunta la direttiva fastcgi_pass (cioè non cadere e far funzionare anche il proxy pass) perché voglio che qualsiasi cosa diversa da POST ritorni al proxy.
Brenton Alker,

2
Nota che ifè generalmente scoraggiato dalla documentazione di Nginx: nginx.com/resources/wiki/start/topics/depth/ifisevil
vog

1
Quindi qual è l'alternativa?
WM,

1
@WM Vedi la mia risposta: serverfault.com/a/823053/175421
vog

@vog, interessante. Modo abbastanza intelligente per farlo. Grazie per la condivisione.
WM,

23

Sebbene sia possibile ottenere questo risultato if, ciò è generalmente scoraggiato dalla documentazione di Nginx , perché ifnon funziona bene con altre direttive. Ad esempio, supponi che GET dovrebbe essere aperto a tutti, mentre POST è solo per utenti autenticati, usando HTTP Basic Auth. Ciò richiederebbe ifdi essere combinato con auth_basic, che non funziona correttamente.

Ecco un'alternativa che funziona senza if. Il trucco è usare "GET" e "POST" come parte dei nomi a monte, quindi questi possono essere risolti con la sostituzione delle variabili:

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

Per combinare questo con HTTP Basic Auth per tutto tranne GET, basta aggiungere un limit_exceptblocco:

  ...
    location /service {
      proxy_pass http://other_$request_method;
      limit_except GET {
        auth_basic ...;
      }
    }
  ...

Il problema con questo approccio è che ora torneremo a 502 gateway errorcausa di no resolver defined to resolve other_HEAD(o qualunque sia il tuo upstream mancante). Sarà più semantico restituire qualcosa del genere 405 method not allowed. C'è un modo per raggiungere questo obiettivo?
James,

1
@James: Questo potrebbe forse essere definito come una nuova domanda, riferendosi a questa. Non ho una risposta per questo dettaglio, ma forse altri.
Vog

0

Questo è quello che ho fatto per far funzionare le cose per me

add_header Allow "GET, POST, HEAD" always;
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
    proxy_pass http://back-end;
}

Hoe passa esattamente tra due endpoint in base al metodo di richiesta?
Base

0

Leggera modifica alla risposta di VOG per includere un gestore predefinito per altri metodi come OPTIONS, PUT, ecc.

    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;
            }
    }

0

Non sono riuscito a ottenere la risposta da @timmmmmy per funzionare, ma mi ha indicato la documentazione della mappa e questo ha funzionato per me:

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;
   }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.