view xml/ru/docs/njs/examples.xml @ 2846:fdf1464e1977

Moved banner to the external file to make partial rollout possible. An idea is to have several banners and show them with different probability specified by split directive in the nginx.conf
author Sergey Budnevitch <sb@waeme.net>
date Tue, 10 May 2022 18:07:27 +0400
parents 24b379907b0f
children 9eadb98ec770
line wrap: on
line source

<?xml version="1.0"?>

<!--
  Copyright (C) Nginx, Inc.
  -->

<!DOCTYPE article SYSTEM "../../../../dtd/article.dtd">

<article name="Примеры использования"
        link="/ru/docs/njs/examples.html"
        lang="ru"
        rev="21">

<section id="summary">

<para>
Примеры работают начиная с версии
<link doc="../njs/changes.xml" id="njs0.4.0">0.4.0</link>.
</para>

</section>


<section id="helloword" name="Hello World">

<para>
<path>nginx.conf</path>:
<example>
events {}

http {
    js_import http.js;

    server {
        listen 8000;

        location / {
            js_content http.hello;
        }
    }
}
</example>
</para>

<para>
<literal>http.js</literal>:
<example>
function hello(r) {
    r.return(200, "Hello world!");
}

export default {hello};
</example>
</para>

</section>


<section id="http_auth" name="HTTP-авторизация">


<section id="jwt" name="Создание HS JWT">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

js_set $jwt http.jwt;
</example>
</para>

<para>
<path>http.js</path>:
<example>
function generate_hs256_jwt(claims, key, valid) {
    var header = { typ: "JWT",  alg: "HS256" };
    var claims = Object.assign(claims, {exp: Math.floor(Date.now()/1000) + valid});

    var s = [header, claims].map(JSON.stringify)
                            .map(v=>v.toString('base64url'))
                            .join('.');

    var h = require('crypto').createHmac('sha256', key);

    return s + '.' + h.update(s).digest('base64url');
}

function jwt(r) {
    var claims = {
        iss: "nginx",
        sub: "alice",
        foo: 123,
        bar: "qq",
        zyx: false
    };

    return generate_hs256_jwt(claims, 'foo', 600);
}

export default {jwt};
</example>
</para>

</section>


<section id="secure_link" name="Создание secure_link хэша">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

js_set $new_foo http.create_secure_link;
#...

location / {
    secure_link $cookie_foo;
    secure_link_md5 "$uri mykey";
    #...
}

location @login {
    add_header Set-Cookie "foo=$new_foo; Max-Age=60";
    return 302 /;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function create_secure_link(r) {
    return require('crypto').createHash('md5')
                            .update(r.uri).update(" mykey")
                            .digest('base64url');
}

export default {create_secure_link};
</example>
</para>

</section>


<section id="jwt_field" name="Получение произвольного поля JWT
                              как значение переменной nginx">

<para>
В данном примере из JWT payload извлекается поле <literal>sub</literal>.
JWT-токен извлекается из заголовка <header>Authorization</header>.
</para>

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

js_set $jwt_payload_sub http.jwt_payload_sub;

server {
    #...

    location /jwt {
        return 200 $jwt_payload_sub;
    }
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function jwt(data) {
    var parts = data.split('.').slice(0,2)
        .map(v=>Buffer.from(v, 'base64url').toString())
        .map(JSON.parse);
    return { headers:parts[0], payload: parts[1] };
}

function jwt_payload_sub(r) {
    return jwt(r.headersIn.Authorization.slice(7)).payload.sub;
    // в случае, если токен передаётся как аргумент "myjwt"
    // return jwt(r.args.myjwt).payload.sub;
}

export default {jwt_payload_sub};
</example>
</para>

</section>

</section>


<section id="http_proxying" name="HTTP-проксирование">


<section id="subrequest" name="Доступ к API из подзапроса">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

keyval_zone zone=foo:10m;
#...

location /keyval {
    js_content http.set_keyval;
}

location /version {
    js_content http.version;
}

location /api {
    api write=on;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
async function set_keyval(r) {
    let res = await r.subrequest('/api/7/http/keyvals/foo',
        { method: 'POST',
          body: JSON.stringify({ foo: 789, bar: "ss dd 00" })});

    if (res.status >= 300) {
        r.return(res.status, res.responseBody);
        return;
    }

    r.return(200);
}

async function version(r) {
    let res = await r.subrequest('/api/7/nginx', { method: 'GET' });

    if (res.status != 200) {
        r.return(res.status);
        return;
    }

    var json = JSON.parse(res.responseBody);
    r.return(200, json.version);
}

export default {set_keyval, version};
</example>
<note>
Директивы <link doc="../http/ngx_http_keyval_module.xml" id="keyval"/>,
<link doc="../http/ngx_http_keyval_module.xml" id="keyval_zone"/>
и <link doc="../http/ngx_http_api_module.xml" id="api"/>
доступны как часть
<commercial_version>коммерческой подписки</commercial_version>.
</note>
</para>

</section>


<section id="fast_response" name="Возвращение самого быстрого ответа от прокси">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

location /start {
    js_content http.content;
}

location /foo {
    proxy_pass http://backend1;
}

location /bar {
    proxy_pass http://backend2;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function content(r) {
    var n = 0;

    function done(res) {
        if (n++ == 0) {
            r.return(res.status, res.responseBody);
        }
    }

    r.subrequest('/foo', r.variables.args, done);
    r.subrequest('/bar', r.variables.args, done);
}

export default {content};
</example>
</para>

</section>


<section id="subrequests_chaining" name="Построение цепочки подзапросов">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

location /start {
    js_content http.content;
}

location /auth {
    proxy_pass http://auth_backend;
}

location /backend {
    proxy_pass http://backend;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
async function content(r) {
    try {
        let reply = await r.subrequest('/auth');
        let response = JSON.parse(reply.responseBody);
        let token = response['token'];

        if (!token) {
            throw new Error("token is not available");
        }

        let backend_reply = await r.subrequest('/backend', `token=${token}`);
        r.return(backend_reply.status, backend_reply.responseBody);

    } catch (e) {
        r.error(e);
        r.return(500);
    }
}

export default {content};
</example>
</para>

</section>

</section>


<section id="misc" name="Разное">


<section id="redirect" name="Внутренняя переадресация">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

location /redirect {
    js_content http.redirect;
}

location @named {
    return 200 named;
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function redirect(r) {
    r.internalRedirect('@named');
}

export default {redirect};
</example>
</para>

</section>


<section id="requests" name="Запись в лог количества запросов от клиента">

<para>
<path>nginx.conf</path>:
<example>
js_import http.js;

js_set $num_requests http.num_requests;

keyval_zone zone=foo:10m;

keyval $remote_addr $foo zone=foo;

log_format bar '$remote_addr [$time_local] $num_requests';
access_log logs/access.log bar;

server {
    listen 8000;

    location / {
        root html;
    }
}
</example>
</para>

<para>
<path>http.js</path>:
<example>
function num_requests(r)
{
    var n = r.variables.foo;
    n = n ? Number(n) + 1 : 1;
    r.variables.foo = n;
    return n;
}

export default {num_requests};
</example>
<note>
Директивы <link doc="../http/ngx_http_keyval_module.xml" id="keyval"/> и
<link doc="../http/ngx_http_keyval_module.xml" id="keyval_zone"/>
доступны как часть
<commercial_version>коммерческой подписки</commercial_version>.
</note>
</para>

</section>

</section>

</article>