Еще в октябре 2020 года Brightcove запустила наш новый бренд - это сопровождалось развертыванием новой сборки www.brightcove.com в Gatsby с использованием Contentful и Netlify. В январе мы переместили наш англоязычный блог, и поэтому нам пришлось найти замену представлению каналов (изначально обслуживаемое через Node / SailsJS / Pug). К счастью, это было несложно благодаря функциям Netlify и Contentful's JavaScript SDK.

RSS-канал - это, по сути, просто длинный XML-файл с элементами, которые извлекают читатели. Таким образом, функция должна делать три вещи:

  1. Получить контент
  2. Соберите и отформатируйте ленту
  3. Вернуть отформатированный фид

Во-первых, внутри нашего проекта Gatsby, развернутого в Netlify, создайте папку / functions / (если вы еще этого не сделали). В папке создайте файл с именем rss-feed.js.

В rss-feed.js начните с импорта API доставки контента Contentful javascript и настройте клиент с вашими кредитами:

const contentful = require(“contentful”);
const client = contentful.createClient({
  space: "SPACE_ID",
  accessToken: process.env.GATSBY_CONTENTFUL_ACCESS_TOKEN
});

Вы можете использовать ту же переменную env, которая используется для доступа к Contentful, что и остальная часть вашего приложения - rad.

Затем мы можем настроить обработчик, который действительно будет отвечать на запрос.

exports.handler = async (event, context) => {
  // get posts from contentful and do stuff
  return {
    statusCode: 200,
    body: "MY FEED WILL GO HERE"
  };
};

Функции Netlify должны возвращать статус и тело. В нашем случае этим телом будет форматированный XML с набором данных. Но сначала давайте добавим вызов Contentful.

const posts = await client.getEntries({
    content_type: "post",
    locale: "en-US",
    order: "-fields.publishedAt"
 });

Это асинхронный вызов настроенного клиента Contentful выше. Наш тип контента был определен как «публикация», и мы ищем публикации на английском языке, поэтому мы передадим языковой стандарт «en-US». Чтобы убедиться, что они возвращаются первыми с самыми последними сообщениями, мы добавляем запрос порядка «-fields.publishedAt» (вы можете использовать любой параметр, который хотите здесь, на основе вашей собственной модели контента).

Теперь, когда мы получаем сообщения, пора создать ленту. Два метода ниже делают именно это: настраивают открывающий и закрывающий теги для всего фида, а также специфичное для элемента форматирование для каждой записи. Спасибо шаблонным литералам за то, что сделали это супер простым.

const sanitize = (string) => {
  const newString = string.replace(/\&/g, "&");
  return newString;
};
const dateString = (dstring) => {
  const target = new Date(dstring);
  return target.toUTCString();
};
const feedWrapper = (contents) => {
  return `<?xml version="1.0" encoding="utf-8" ?>
    <rss version="2.0" xml:base="https://site.com/en/blog/feed" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>My Blog - The Leading Blog</title>
        <link>https://site.com/en/blog</link>
        <description>Superior Posts</description>
        <language>en</language>
        ${contents}
    </channel>
    </rss>
    `;
};
const stringify = (obj) => {
  const template = (entry, id) => `
        <item>
            <title>${sanitize(entry.title) || ""}</title>
            <link>https://site.com/en/blog/${entry.slug || ""}</link>
            <category>https://site.com/en/blog/${
              entry.category.fields.slug
            }</category>
            <pubDate>${dateString(entry.publishedAt)}</pubDate>
            <description>${sanitize(entry.body)}</description>
            <dc:creator>${entry.author.fields.name}</dc:creator>
            <guid>https://site.com/en/blog/${entry.slug}</guid>
        </item>
    `;
  if (obj && obj.fields && Object.keys(obj.fields).length > 0) {
    return template(obj.fields, obj.sys.id);
  } else {
    return "";
  }
};

Хорошо, давайте поговорим о некоторых из них. Методы sanitize и dateString просто очищают контент, возвращаемый Contentful; плохие символы HTML (в частности, «&») рассердят читателей ленты, поэтому santize заменяет их. Чтобы преобразовать метку времени в миллисекундах в удобочитаемую дату / время, метод dateString возвращает строку в формате UTC - опять же, чтобы сделать значение ‹pubDate› действительным.

Метод feedWrapper содержит открывающие и закрывающие теги каналов и принимает параметр contents - этот параметр будет списком записей, созданных методом stringify. Давайте составим этот список:

const buildFeed = (posts) => {
  let entries = "";
  posts.items.forEach((item, index) => {
    if (item.fields && Object.keys(item.fields).length > 0) {
      entries += stringify(item);
    }
  });
  return feedWrapper(entries);
};

Мы создали пустую строку из записей, которая добавляется циклом forEach элементов сообщения, которые мы передаем методу из содержательного ответа. Мы проверяем, есть ли у объекта сообщения поля с нужным нам содержимым, и генерируем строку шаблона для каждого из них. Затем метод передает построенный список строк ‹item›… .. ‹/item› методу feedWrapper и возвращает скомпилированный канал XML.

Собираем все вместе, это выглядит так:

const contentful = require("contentful");
const client = contentful.createClient({
  space: "SPACE_ID",
  accessToken: process.env.GATSBY_CONTENTFUL_ACCESS_TOKEN
});
const feedWrapper = (contents) => {
  return `<?xml version="1.0" encoding="utf-8" ?>
    <rss version="2.0" xml:base="https://site.com/en/blog/feed" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>My Blog - The Leading Blog</title>
        <link>https://site.com/en/blog</link>
        <description>Superior Posts</description>
        <language>en</language>
        ${contents}
    </channel>
    </rss>
    `;
};
const sanitize = (string) => {
  const newString = string.replace(/\&/g, "&amp;");
  return newString;
};
const dateString = (dstring) => {
  const target = new Date(dstring);
  return target.toUTCString();
};
const stringify = (obj) => {
  const template = (entry, id) => `
        <item>
            <title>${sanitize(entry.title) || ""}</title>
            <link>https://site.com/en/blog/${entry.slug || ""}</link>
            <category>https://site.com/en/blog/${
              entry.category.fields.slug
            }</category>
            <pubDate>${dateString(entry.publishedAt)}</pubDate>
            <description>${sanitize(entry.body || "")}</description>
            <dc:creator>${entry.author.fields.name}</dc:creator>
            <guid>https://site.com/en/blog/${entry.slug}</guid>
        </item>
    `;
  if (obj && obj.fields && Object.keys(obj.fields).length > 0) {
    return template(obj.fields, obj.sys.id);
  } else {
    return "";
  }
};
const buildFeed = (posts) => {
  let entries = "";
  posts.items.forEach((item, index) => {
    if (item.fields && Object.keys(item.fields).length > 0) {
      entries += stringify(item);
    }
  });
  return feedWrapper(entries);
};
exports.handler = async (event, context) => {
  // get posts from contentful
  const posts = await client.getEntries({
    content_type: "post",
    locale: "en-US",
    order: "-fields.publishedAt"
  });
  const formatted = posts ? buildFeed(posts) : "";
  return {
    statusCode: 200,
    body: formatted
  };
};

Функция ожидает публикации, создает ленту, перебирая элементы в цикле, затем вставляет элементы в оболочку XML и возвращает ее в качестве тела ответа. Теперь, чтобы сделать это маршрутом в вашем приложении netlify, вы можете создать перенаправление Netlify. В нашем случае мы уже широко используем файл /static/_redirects, поэтому нам просто нужно было добавить новую строку:

/en/blog/feed/all   /.netlify/functions/rss-feed  200!

Теперь мы можем указать пользователям URL-адрес en/blog/feed/all, и он выполнит функцию для получения сообщений и возврата xml: https://www.brightcove.com/en/blog/feed/all

Это был довольно быстрый перенос, и я уверен, что в канал можно добавить больше (медиаресурсы и т. Д.), Но при этом наш канал заработал без особых проблем. Я удалил несколько дополнительных библиотек, которые нам пришлось использовать для очистки данных ответа, но стоит отметить, что если вы получаете Markdown обратно из CMS, его следует проанализировать и очистить - я обнаружил markdown- it и string , чтобы быть полезными для нормализации данных во что-то, что предпочитает парсер RSS.

Пожалуйста, выключите звук, если я хочу добавить сюда что-нибудь еще! Спасибо за прочтение!