Proxy Nginx pelo método de solicitação

17

É possível / como posso configurar um bloco de localização do Nginx para proxy para back-end diferentes, dependendo do método de solicitação (por exemplo, GET / POST)?

O motivo é que, atualmente, estou lidando com os 2 métodos em 2 URLs diferentes (um via proxy http e outro via fcgi) e estou tentando torná-lo mais "REST"; portanto, o ideal é que GETting the resource retorne a lista , enquanto o POST no mesmo recurso deve ser adicionado à lista.

Brenton Alker
fonte

Respostas:

27

Eu não uso essa configuração, mas com base nos exemplos aqui :

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

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

Se você estiver escrevendo seu próprio aplicativo, considere também verificar GET / POST e enviar cabeçalhos X-Accel-Redirect para distribuir o transporte dos arquivos para o nginx.

Jason
fonte
O bloco GET é um proxy_pass no meu caso, mas, caso contrário, isso funciona. No momento, não estou usando o segundo bloco if, o nginx parece estar parando o "processamento" quando a diretiva fastcgi_pass é atingida (ou seja, não está sendo executada e executada a passagem do proxy também) porque eu quero que algo diferente de POST seja revertido para o proxy.
Brenton Alker
2
Observe que isso ifgeralmente é desencorajado pela documentação do Nginx: nginx.com/resources/wiki/start/topics/depth/ifisevil
vog
1
Então, qual é a alternativa?
WM
1
@WM Veja minha resposta: serverfault.com/a/823053/175421
vog
@vog, Interessante. Uma maneira bem inteligente de fazer isso. Obrigado por compartilhar.
WM
23

Embora você possa conseguir isso if, isso geralmente é desencorajado pela documentação do Nginx , porque ifnão funciona bem com outras diretivas. Por exemplo, suponha que GET deva ser aberto para todos, enquanto POST é apenas para usuários autenticados, usando HTTP Basic Auth. Isso exigiria ifser combinado com auth_basic, o que não funciona corretamente.

Aqui está uma alternativa que funciona sem if. O truque é usar "GET" e "POST" como parte dos nomes upstream, para que possam ser resolvidos por substituição de variável:

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

Para combinar isso com o HTTP Basic Auth para tudo, menos GET, basta adicionar um limit_exceptbloco:

  ...
    location /service {
      proxy_pass http://other_$request_method;
      limit_except GET {
        auth_basic ...;
      }
    }
  ...
vog
fonte
O problema com essa abordagem é que agora retornaremos 502 gateway errordevido a no resolver defined to resolve other_HEAD(ou seja lá qual for a sua falta de upstream). Será mais semântico retornar algo como 405 method not allowed. Existe uma maneira de conseguir isso?
James
1
@ James: Isso talvez deva ser formulado como uma nova pergunta, referente a essa. Eu não tenho uma resposta para esse detalhe, mas talvez outros também.
vog 02/12
0

Isto é o que eu fiz para fazer as coisas funcionarem para mim

add_header Allow "GET, POST, HEAD" always;
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
    proxy_pass http://back-end;
}
Mansur Ali
fonte
Hoe exatamente isso alterna entre dois pontos de extremidade com base no método de solicitação?
Básico
0

Altere ligeiramente a resposta do vog para incluir um manipulador padrão para outros 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
fonte
0

Não consegui obter a resposta do @timmmmmy para funcionar, mas ele me indicou a documentação do mapa e isso funcionou para mim:

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
fonte