Proxy Nginx por método de solicitud

17

¿Es posible / cómo puedo configurar un bloque de ubicación Nginx para proxy a diferentes backends dependiendo del método de solicitud (es decir, GET / POST)?

La razón es que actualmente estoy manejando los 2 métodos en 2 URL diferentes (uno a través de proxy HTTP y el otro a través de fcgi) y estoy tratando de hacerlo más "REST", así que lo ideal sería que OBTENGA el recurso para devolver la lista , mientras que PUBLICAR en el mismo recurso debe agregar a la lista.

Brenton Alker
fuente

Respuestas:

27

No uso esta configuración, pero según los ejemplos aquí :

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

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

Si está escribiendo su propia aplicación, también puede considerar verificar GET / POST en ella y enviar encabezados X-Accel-Redirect para entregar el transporte de los archivos a nginx.

Jason
fuente
El bloque GET es un proxy_pass en mi caso, pero de lo contrario eso funciona. En este momento no estoy usando el segundo bloque if, parece que nginx está deteniendo el "procesamiento" cuando se alcanza la directiva fastcgi_pass (es decir, no se cae y ejecuta el paso proxy también) porque quiero otra cosa que no sea POST para revertir al poder.
Brenton Alker
2
Tenga en cuenta que ifla documentación de Nginx generalmente desalienta: nginx.com/resources/wiki/start/topics/depth/ifisevil
vog
1
¿Entonces cual es la alternativa?
WM
1
@WM Vea mi respuesta: serverfault.com/a/823053/175421
vog
@vog, interesante. Una forma bastante inteligente de hacerlo. Gracias por compartir.
WM
23

Aunque podría lograr esto con if, la documentación de Nginx generalmente desalienta esto , porque ifno funciona bien con otras directivas. Por ejemplo, suponga que GET debe estar abierto para todos, mientras que POST es solo para usuarios autenticados, utilizando HTTP Basic Auth. Eso requeriría ifcombinarse con auth_basic, lo que no funciona correctamente.

Aquí hay una alternativa que funciona sin if. El truco consiste en utilizar "GET" y "POST" como parte de los nombres ascendentes, por lo que estos pueden abordarse mediante la sustitución de variables:

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

Para combinar esto con HTTP Basic Auth para todo menos GET, solo agregue un limit_exceptbloque:

  ...
    location /service {
      proxy_pass http://other_$request_method;
      limit_except GET {
        auth_basic ...;
      }
    }
  ...
vog
fuente
El problema con este enfoque es que ahora regresaremos 502 gateway errorpor no resolver defined to resolve other_HEAD(o lo que sea que falte). Será más semántico devolver algo como 405 method not allowed. ¿Hay alguna manera de lograr esto?
James
1
@James: Esto quizás debería expresarse como una nueva pregunta, refiriéndose a esta. No tengo una respuesta para este detalle, pero tal vez otros para.
vog
0

Esto es lo que hice para que las cosas funcionen para mí.

add_header Allow "GET, POST, HEAD" always;
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
    proxy_pass http://back-end;
}
Mansur Ali
fuente
¿Cómo cambia exactamente eso entre dos puntos finales según el método de solicitud?
Básico
0

Ligero cambio en la respuesta de vog para incluir un controlador predeterminado para otros métodos como 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
fuente
0

No pude obtener la respuesta de @timmmmmy para trabajar, pero me señaló la documentación del mapa y esto funcionó para mí:

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
fuente