Proxy Nginx par méthode de demande

17

Est-il possible / comment puis-je configurer un bloc d'emplacement Nginx pour un proxy vers différents backends en fonction de la méthode de demande (c'est-à-dire GET / POST)?

La raison en est que je gère actuellement les 2 méthodes à 2 URL différentes (une via le proxy http et l'autre via fcgi) et j'essaie de le rendre plus "REST", donc, idéalement, j'aimerais obtenir la ressource pour renvoyer la liste , tandis que le POST sur la même ressource devrait s'ajouter à la liste.

Brenton Alker
la source

Réponses:

27

Je n'utilise pas cette configuration, mais sur la base des exemples ici :

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

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

Si vous écrivez votre propre application, vous pouvez également envisager d'y vérifier GET / POST et d'envoyer des en- têtes X-Accel-Redirect pour transférer le transport des fichiers vers nginx.

Jason
la source
Le bloc GET est un proxy_pass dans mon cas, mais sinon cela fonctionne. Pour le moment, je n'utilise pas le second bloc if, nginx semble arrêter le "traitement" lorsque la directive fastcgi_pass est atteinte (c'est-à-dire ne pas tomber et exécuter la passe proxy également) parce que je veux que autre chose que POST revienne au proxy.
Brenton Alker
2
Notez que cela ifest généralement déconseillé par la documentation Nginx: nginx.com/resources/wiki/start/topics/depth/ifisevil
vog
1
Alors, quelle est l'alternative?
WM
1
@WM Voir ma réponse: serverfault.com/a/823053/175421
vog
@vog, intéressant. Une façon assez intelligente de le faire. Merci d'avoir partagé.
WM
23

Bien que vous puissiez y parvenir avec if, cela est généralement déconseillé par la documentation Nginx , car ifne fonctionne pas bien avec d'autres directives. Par exemple, supposons que GET doit être ouvert à tout le monde, alors que POST est uniquement destiné aux utilisateurs authentifiés, en utilisant HTTP Basic Auth. Cela nécessiterait ifd'être combiné avec auth_basic, ce qui ne fonctionne pas correctement.

Voici une alternative qui fonctionne sans if. L'astuce consiste à utiliser "GET" et "POST" dans le cadre des noms en amont, afin que ceux-ci puissent être traités par substitution de variable:

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

Pour combiner cela avec HTTP Basic Auth pour tout sauf GET, ajoutez simplement un limit_exceptbloc:

  ...
    location /service {
      proxy_pass http://other_$request_method;
      limit_except GET {
        auth_basic ...;
      }
    }
  ...
vog
la source
Le problème avec cette approche est maintenant que nous reviendrons à 502 gateway errorcause de no resolver defined to resolve other_HEAD(ou quel que soit votre manquant en amont). Il sera plus sémantique de renvoyer quelque chose comme 405 method not allowed. Existe-t-il un moyen d'y parvenir?
James
1
@James: Cela pourrait peut-être être formulé comme une nouvelle question, se référant à celle-ci. Je n'ai pas de réponse pour ce détail, mais peut-être pour d'autres.
vog
0

C'est ce que j'ai fait pour que les choses fonctionnent pour moi

add_header Allow "GET, POST, HEAD" always;
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
    proxy_pass http://back-end;
}
Mansur Ali
la source
Hoe, cela change-t-il exactement entre deux points de terminaison en fonction de la méthode de demande?
Basic
0

Légère modification de la réponse de vog pour inclure un gestionnaire par défaut pour d'autres méthodes comme OPTIONS, PUT, etc.

    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
la source
0

Je n'ai pas pu obtenir la réponse de @timmmmmy au travail, mais cela m'a indiqué la documentation de la carte et cela a fonctionné pour moi:

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
la source