среда, 28 августа 2013 г.

Конфигурация nginx: вложенные if

Как известно, вложенные if в конфигурации nginx запрещены, поскольку это вовсе не языковая конструкция nginx, а обычная директива из модуля rewrite, использование которой в ее же собственном контексте if не предусмотрено - так уж она сделана. Использование списка условий, разделенных логическими операторами и и или тоже не предусмотрено. Обычно для эмуляции вложенных условий с использованием директивы if предлагают использовать регулярные выражения. Например if в
        location /test_if.html {
            set $cond $host::$arg_a;
            if ($cond ~* '^localhost::.+') {
                echo "Matched: host = '$host', a = '$arg_a'";
                break;
            }
            echo "Not matched: host = '$host', a = '$arg_a'";
        }
проверяет, что имя, указанное в HTTP хедере Host, соответствует значению localhost и в URI присутствует аргумент a (в этом случае переменная $arg_a будет не пустой).

К сожалению, условие в директиве if имеет ограничения. По крайней мере, это не выражение, и, как я уже сказал, оно не может быть составлено в виде цепочки выражений, разделенных логическими операторами. Кроме того, в случае сравнения чего-то с регулярным выражением, это что-то может быть только именем переменной, именно поэтому нам пришлось ввести новую переменную $cond, а не записать это просто как
            if ($host::$arg_a ~* '^localhost::.+') {
В данном случае nginx просто не запустится, заявив unknown "host::$arg_a" variable. Выражение для регулярного выражения в условии if также имеет ограничение: в нем нельзя использовать переменные - они просто не будут расширяться. Но что тогда делать, если мы хотим сравнить $host на соответствие не константному выражению localhost, а значению, определенному в какой-нибудь переменной, например $goodhost? Ответ - использовать другую переменную $cond и другое регулярное выражение.
        location /test_if.html {
            set $goodhost 'localhost';
            set $cond $host::$goodhost::$arg_a;
            if ($cond ~* '^(.*)::\1::.+') {
                echo "Matched: host = '$host', a = '$arg_a'";
                break;
            }
            echo "Not matched: host = '$host', a = '$arg_a'";
        }
В данном примере мы соединили через произвольно выбранный нами разделитель :: три переменных $host, $goodhost и $arg_a и присвоили это значение переменной $cond. А регулярное выражение, с которым мы сопоставляем это значение, проверяет, что его первая часть (до разделителя ::) и вторая часть (до второго разделителя ::) равны, а последняя часть (после второго разделителя) не пуста.

Идея понятна, последний пример - трансляция псевдокода
            if ($host == $goodhost && not_empty($arg_a)) {
                if ($arg_a == 'ok') {
                    echo "Matched: host = '$host', a is Ok";
                } else {
                    echo "Matched: host = '$host', a is '$arg_a'";
                }
            } else {
                echo "Not matched: host = '$host', a = '$arg_a'";
            }
Результат трансляции:
        location /test_if.html {
            set $goodhost 'localhost';
            set $cond $host::$goodhost::$arg_a;
            if ($cond ~* '^(.*)::\1::ok$') {
                echo "Matched: host = '$host', a is Ok";
                break;
            }
            if ($cond ~* '^(.*)::\1::.+$') {
                echo "Matched: host = '$host', a = '$arg_a'";
                break;
            }
            echo "Not matched: host = '$host', a = '$arg_a'";
        }

2 комментария:

  1. На else nginx ругается: Starting nginx: nginx: [emerg] unknown directive "else" in /etc/nginx/sites-enabled/domU-test-1

    ОтветитьУдалить
    Ответы
    1. В nginx нет else. Здесь список всех директив rewrite модуля nginx: http://nginx.org/ru/docs/http/ngx_http_rewrite_module.html

      Удалить