Cloudflare Workers - СПА с Вуэйсом - PullRequest
0 голосов
/ 17 октября 2019

Здравствуйте. Я развернул свое приложение Vue.js для работников Cloudflare, используя следующие команды:

wrangler generate --site
wrangler publish --env dev

Это мой wrangler.toml:

account_id = "xxx"
name = "name"
type = "webpack"
workers_dev = true

[site]
bucket = "./dist"
entry-point = "workers-site"

[env.dev]
name = "name"
route = "xxx.com/*"
zone_id = "XXX"
account_id = "XXX"

С веб-сайтом все в порядке ив прямом эфире на «xxx.com», но когда я обновляю страницу на любом другом маршруте, я получаю это сообщение об ошибке:

не удалось найти es-es / index.html в вашем пространстве имен контента

Или, например:

не удалось найти категорию / 65 / index.html в пространстве имен вашего контента

В nginx мне пришлось создать .htaccess, но я понятия не имею, как заставить его работать здесь.

Это мой index.js на случай, если он поможет:

import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'

/**
 * The DEBUG flag will do two things that help during development:
 * 1. we will skip caching on the edge, which makes it easier to
 *    debug.
 * 2. we will return an error message on exception in your Response rather
 *    than the default 404.html page.
 */
const DEBUG = false

addEventListener('fetch', event => {
  try {
    event.respondWith(handleEvent(event))
  } catch (e) {
    if (DEBUG) {
      return event.respondWith(
        new Response(e.message || e.toString(), {
          status: 500,
        }),
      )
    }
    event.respondWith(new Response('Internal Error', { status: 500 }))
  }
})

async function handleEvent(event) {
  const url = new URL(event.request.url)
  let options = {}

  /**
   * You can add custom logic to how we fetch your assets
   * by configuring the function `mapRequestToAsset`
   */
  // options.mapRequestToAsset = handlePrefix(/^\/docs/)

  try {
    if (DEBUG) {
      // customize caching
      options.cacheControl = {
        bypassCache: true,
      }
    }
    return await getAssetFromKV(event, options)
  } catch (e) {
    // if an error is thrown try to serve the asset at 404.html
    if (!DEBUG) {
      try {
        let notFoundResponse = await getAssetFromKV(event, {
          mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/404.html`, req),
        })

        return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
      } catch (e) {}
    }

    return new Response(e.message || e.toString(), { status: 500 })
  }
}

/**
 * Here's one example of how to modify a request to
 * remove a specific prefix, in this case `/docs` from
 * the url. This can be useful if you are deploying to a
 * route on a zone, or if you only want your static content
 * to exist at a specific path.
 */
function handlePrefix(prefix) {
  return request => {
    // compute the default (e.g. / -> index.html)
    let defaultAssetKey = mapRequestToAsset(request)
    let url = new URL(defaultAssetKey.url)

    // strip the prefix from the path for lookup
    url.pathname = url.pathname.replace(prefix, '/')

    // inherit all other props from the default request
    return new Request(url.toString(), defaultAssetKey)
  }
}

1 Ответ

2 голосов
/ 17 октября 2019

Как вы знаете, Vue.js (как и многие другие платформы SPA) ожидает, что для любого пути, который не сопоставлен с конкретным файлом, сервер возвращается к обслуживанию корневого файла /index.html. Затем Vue выполнит маршрутизацию в JavaScript на стороне браузера. Вы упомянули, что знаете, как выполнить этот откат с .htaccess, но как мы можем сделать это с Workers?

Хорошие новости: В Workers мы можем написать код, который будет делать все, что мы захотим!

На самом деле, рабочий код уже имеет специальный блок кода для обработки ошибок «404 not found». Одним из способов решения этой проблемы было бы изменить этот блок кода так, чтобы вместо возврата ошибки 404 он возвращал /index.html.

Код, который мы хотим изменить, состоит в следующем:

  } catch (e) {
    // if an error is thrown try to serve the asset at 404.html
    if (!DEBUG) {
      try {
        let notFoundResponse = await getAssetFromKV(event, {
          mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/404.html`, req),
        })

        return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
      } catch (e) {}
    }

    return new Response(e.message || e.toString(), { status: 500 })
  }

Мы хотим изменить его на:

  } catch (e) {
    // Fall back to serving `/index.html` on errors.
    return getAssetFromKV(event, {
      mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/index.html`, req),
    })
  }

Это должно сработать.


Однако вышеприведенное решение имеет небольшую проблему: для любой HTML-страницы (кроме корня), он выполнит два поиска, сначала по определенному пути, и только после этого будет искать /index.html как запасной вариант. Эти поиски выполняются довольно быстро, но, возможно, мы сможем ускорить процесс, сделав их немного умнее и предварительно обнаружив HTML-страницы на основе URL.

Для этого нам нужно настроить функцию mapRequestToAsset. Вы можете увидеть подсказку об этом в комментарии в коде:

  /**
   * You can add custom logic to how we fetch your assets
   * by configuring the function `mapRequestToAsset`
   */
  // options.mapRequestToAsset = handlePrefix(/^\/docs/)

Давайте продолжим и будем его использовать. Замените приведенный выше комментарий следующим:

  options.mapRequestToAsset = req => {
    // First let's apply the default handler, which we imported from
    // '@cloudflare/kv-asset-handler' at the top of the file. We do
    // this because the default handler already has logic to detect
    // paths that should map to HTML files, for which it appends
    // `/index.html` to the path.
    req = mapRequestToAsset(req)

    // Now we can detect if the default handler decided to map to
    // index.html in some specific directory.
    if (req.url.endsWith('/index.html')) {
      // Indeed. Let's change it to instead map to the root `/index.html`.
      // This avoids the need to do a redundant lookup that we know will
      // fail.
      return new Request(`${new URL(req.url).origin}/index.html`, req)
    } else {
      // The default handler decided this is not an HTML page. It's probably
      // an image, CSS, or JS file. Leave it as-is.
      return req
    }
  }

Теперь код обнаруживает только HTML-запросы и заменяет их на корень /index.html, поэтому не нужно тратить время на поиск файла, который не существует толькопоймать полученную ошибку. Для других типов файлов (изображений, JS, CSS и т. Д.) Код не будет изменять имя файла.

...