API 浏览器
Quasar CLI with Webpack - @quasar/app-webpack
SSR 404 和 500 错误处理

服务端渲染 (SSR) 中 404 和 500 错误的处理与其他模式(如单页渲染 (SPA))略有不同。如果你查看 /src-ssr/middlewares/render.js,你会注意到以下部分:

¥The handling of the 404 & 500 errors on SSR is a bit different than on the other modes (like SPA). If you check out /src-ssr/middlewares/render.js, you will notice the following section:

/src-ssr/middlewares/render.js

import { defineSsrMiddleware } from '#q-app/wrappers'

// This middleware should execute as last one
// since it captures everything and tries to
// render the page with Vue

export default defineSsrMiddleware(({ app, resolve, render, serve }) => {
  // we capture any other Express route and hand it
  // over to Vue and Vue Router to render our page
  app.get(resolve.urlPath('*'), (req, res) => {
    res.setHeader('Content-Type', 'text/html')

    render(/* the ssrContext: */ { req, res })
      .then(html => {
        // now let's send the rendered html to the client
        res.send(html)
      })
      .catch(err => {
        // oops, we had an error while rendering the page

        // we were told to redirect to another URL
        if (err.url) {
          if (err.code) {
            res.redirect(err.code, err.url)
          } else {
            res.redirect(err.url)
          }
        } else if (err.code === 404) {
          // hmm, Vue Router could not find the requested route

          // Should reach here only if no "catch-all" route
          // is defined in /src/routes
          res.status(404).send('404 | Page Not Found')
        } else if (process.env.DEV) {
          // well, we treat any other code as error;
          // if we're in dev mode, then we can use Quasar CLI
          // to display a nice error page that contains the stack
          // and other useful information

          // serve.error is available on dev only
          serve.error({ err, req, res })
        } else {
          // we're in production, so we should have another method
          // to display something to the client when we encounter an error
          // (for security reasons, it's not ok to display the same wealth
          // of information as we do in development)

          // Render Error Page on production or
          // create a route (/src/routes) for an error page and redirect to it
          res.status(500).send('500 | Internal Server Error')

          if (process.env.DEBUGGING) {
            console.error(err.stack)
          }
        }
      })
  })
})

以上部分是在捕获其他可能的请求(例如 /public 文件夹、manifest.json 和 Service Worker 等)后编写的。我们在这里使用 Vue 和 Vue Router 渲染页面。

¥The section above is written after catching the other possible requests (like for /public folder, the manifest.json and service worker, etc). This is where we render the page with Vue and Vue Router.

注意事项(Things to be aware of)

¥Things to be aware of

我们将讨论一些你需要注意的架构决策。选择最适合你应用的路径。

¥We’ll discuss some architectural decisions that you need to be aware of. Choose whatever fits your app best.

错误 404(Error 404)

¥Error 404

如果你在 Vue Router /src/router/routes.js 文件上定义了等效的 404 路由(如下所示),那么上面示例中的 if (err.code === 404) { 部分永远不会是 true,因为 Vue Router 已经处理了它。

¥If you define an equivalent 404 route on your Vue Router /src/router/routes.js file (like below), then if (err.code === 404) { part from the example above will NEVER be true since Vue Router already handled it.

// Example of route for catching 404 with Vue Router
{ path: '/:catchAll(.*)*', component: () => import('pages/Error404.vue') }

错误 500(Error 500)

¥Error 500

在页面顶部的 /src-ssr/middlewares/render.js 示例中,请注意,如果 Web 服务器遇到任何渲染错误,我们会向客户端 (‘500 | 内部服务器错误’) 发送一个简单的字符串。如果你想要显示一个美观的页面,你可以:

¥On the /src-ssr/middlewares/render.js example at the top of the page, notice that if the webserver encounters any rendering error, we send a simple string back to the client (‘500 | Internal Server Error’). If you want to show a nice page instead, you could:

  1. /src/router/routes.js 中添加特定路由,例如:

    ¥Add a specific route in /src/router/routes.js, like:

{ path: 'error500', component: () => import('pages/Error500.vue') }
  1. 编写 Vue 组件来处理此页面。在本例中,我们创建 /src/pages/Error500.vue

    ¥Write the Vue component to handle this page. In this example, we create /src/pages/Error500.vue

  2. 然后在 /src-ssr/middlewares/render.js 中:

    ¥Then in /src-ssr/middlewares/render.js:

if (err.url) { ... }
else if (err.code === 404) { ... }
else {
  // We got a 500 error here;
  // We redirect to our "error500" route newly defined at step #1.
  res.redirect(resolve.urlPath('error500')) // keep account of publicPath though!
}

危险

唯一需要注意的是,你需要确保在渲染 ‘/error500’ 路由时不会再次出现 500 错误,否则你的应用会陷入无限循环!

¥The only caveat is that you need to be sure that while rendering ‘/error500’ route you don’t get another 500 error, which would put your app into an infinite loop!

避免这种情况的完美方法是直接从 /src-ssr/middlewares/render.js 返回错误 500 页面的 HTML(作为字符串):

¥A perfect approach to avoid this would simply be to directly return the HTML (as String) of the error 500 page from /src-ssr/middlewares/render.js:

res.status(500).send(`<html>....</html>`)