Несколько недель назад я написал в блоге о создании универсального обработчика форм с помощью OpenWhisk. Идея заключалась в том, что вы могли указать любую форму на действие, и она собирала данные формы и отправляла результаты по электронной почте. Одна из проблем с моим действием заключалась в том, что оно работало только через HTTP-вызов к конечной точке. Это сделало его пригодным для простого вызова Ajax из JavaScript, но упустило одну из крутых функций, которые предоставляет большинство сервисов обработки форм — возможность перенаправления на другой URL-адрес после завершения.

Оказывается, поддержка теперь доступна в OpenWhisk в виде новой функции веб-действий. Сотрудник IBM Родрик Раббах написал отличный пост об этой функции здесь: Бессерверные обработчики HTTP с OpenWhisk. Также обязательно ознакомьтесь с его продолжением здесь: Веб-действия: бессерверные веб-приложения с OpenWhisk.

Инструкцию повторять не буду, но по сути она сводится к нескольким изменениям:

  • Во-первых, при создании (или обновлении) действия добавьте --annotation web-export true
  • Во-вторых, выяснить URL. Ваш URL-адрес будет https://openwhisk.ng.bluemix.net/api/v1/experimental/web/NAMESPACE/PACKAGE/ACTION.TYPE. Если вы не используете пакет для своего действия, используйте default. Значение TYPE может быть одним из json, html, text или http.

В принципе, это все. (Есть еще, но опять же, см. пост Родрика). Учитывая, что мои действия в основном уже были выполнены, модификации были довольно простыми. Я поделюсь всем действием в конце, а вот моды, которые я добавил.

Сначала я просто ищу новый параметр _next. Если указано, значит, мы хотим перенаправить после обработки формы. Я сделал это необязательным, чтобы по-прежнему поддерживать версию Ajax, которую я создал изначально.

let next = '';
if(args['_next']) next = args['_next'];

Затем я просто ищу это, когда закончу:

if(next === '') {
    resolve({result:'Ok'});
} else {
    resolve({
        headers: { location: next},
        code: 302
    });
}

Последним шагом было обновление моего действия с помощью нового кода и этого нового флага, чтобы открыть его как веб-действие. Я изменил свою форму, чтобы пропустить использование JavaScript для отправки и просто опубликовать новый URL:

<form id="contactForm"
action = "https://openwhisk.ng.bluemix.net/api/v1/experimental/web/[email protected]_My%20Space/default/sendmail.http" 
method="post">
<input type="hidden" name="_next" value="https://www.raymondcamden.com">

Обратите внимание на использование HTTP в качестве типа контента. Я не отправляю обратно HTML, а заголовки HTTP. Если бы я хотел создать динамическую страницу «спасибо», я бы вместо этого использовал расширение HTML. Значение _next, которое я использовал, — это просто мой блог, но в реальной ситуации это была бы страница, специально предназначенная для благодарности пользователю за его отзыв.

И это все. Это отличное дополнение к платформе, и мне не терпится еще немного попинать шины. Вот полный источник действия, и, как всегда, если у вас есть какие-либо вопросы, комментарии или предложения, просто добавьте комментарий ниже.

//SendGrid API Key
var SG_KEY = 'SG.secretkeysbetweenyouandme';
//people we are allowed to email
var RECIPS = ["[email protected]","[email protected]"];
var helper = require('sendgrid').mail;
function main(args) {
    let from_email = new helper.Email(RECIPS[0]);
    let to_email = new helper.Email(RECIPS[0]);
    let next = '';
    if(args['_next']) next = args['_next'];
    /*
    Ok, so args will be our form data. Look for a few special item
    */
    if(args["_from"]) {
        from_email = new helper.Email(args["_from"]);
    }
    if(args["_to"]) {
        if(RECIPS.indexOf(args["_to"]) === -1) {
            return {error:"Invalid _to address: "+args["_to"]};
        } else {
            to_email = new helper.Email(args["_to"]);
        }
    }
    let subject = 'Form Submission';
    if(args["_subject"]) {
        subject = args["_subject"];
    }
    /*
    Now loop through the rest of the args to create the form submission email.
    */
    //todo: make date a bit prettier
    let date = new Date();
    let content = `
Form Submitted at ${date}
--------------------------------
`;
    for(let key in args) {
        //blanket ignore if _*
        if(key.indexOf("_") != 0) {
            content += `
${key}:         ${args[key]}
`;
        }
    }
    let mailContent = new helper.Content('text/plain', content);
    let mail = new helper.Mail(from_email, subject, to_email, mailContent);
    let sg = require('sendgrid')(SG_KEY);
    return new Promise( (resolve, reject) => {
        var request = sg.emptyRequest({
            method: 'POST',
            path: '/v3/mail/send',
            body: mail.toJSON()
        });
        
        sg.API(request, function(error, response) {
            if(error) {
                console.log(error.response.body);
                reject({error:error.message}) 
            } else {
                console.log('got to good result, next is '+next);
                /*
                new logic - handle possible redirect
                */
                if(next === '') {
                    resolve({result:'Ok'});
                } else {
                    resolve({
                        headers: { location: next},
                        code: 302
                    });
                }
            }
        });
    });
}
exports.main = main;

Эта запись первоначально появилась в моем блоге по адресу https://www.raymondcamden.com/2017/02/15/building-a-form-handler-service-in-openwhisk-part-two