2

I'm trying to create an SSG site with Nuxt.js. When I access a route that isn't set in the generate property of nuxt.config.js, I want to display the contents of a 404 page without changing the URL.(using htaccess)

The following is the site under construction

http://we-are-sober.d3v-svr.com/xxxx

This is working as expected.

http://we-are-sober.d3v-svr.com/user/xxxx

This does not work as expected.

The contents of page 404 are displayed for a moment, but soon after that, the process based on the dynamic route of "user/_id.vue" is executed.

The point of the problem is that the non-existent route behaves as if it exists. Does anyone know how to solve this problem?

Here is the source code. https://github.com/yhirochick/rewrite_test

404.vue

https://github.com/yhirochick/rewrite_test/blob/master/pages/404.vue

user/_id.vue

https://github.com/yhirochick/rewrite_test/blob/master/pages/user/_id.vue

nuxt.config.js

https://github.com/yhirochick/rewrite_test/blob/master/nuxt.config.js#L43-L45

.htaccess

https://github.com/yhirochick/rewrite_test/blob/master/static/.htaccess

I am Japanese. The above text is based on Google Translate. It may be difficult to understand, but thank you.

kissu
  • 40,416
  • 14
  • 65
  • 133
yhirochick
  • 93
  • 1
  • 11
  • If you have a `user/_id`, route and input `/user/xxx`, `xxx` is treated as being the `id`. You will need to make a validation when you reach the page, rather than trying to make a redirect elsewhere. You could even make a call to the backend, see if the input (here: `xxx`) exists and if it does not, redirect to a 404. Is it something that you may be interested into? – kissu Jul 17 '21 at 09:41
  • @kissu The site I'm trying to build uses a pay-as-you-go API. Therefore, the API executed by the client should be as small as possible. Requests from clients are used only for the keyword search function. Therefore, the client does not check the existence of id. Instead, `nuxt generate` will only generate pages based on existing ids. – yhirochick Jul 18 '21 at 08:01
  • Simillar problem is here. https://github.com/nuxt/nuxt.js/issues/7051#issuecomment-618904076 – yhirochick Jul 18 '21 at 17:00
  • @kissu Thank you for investigating in detail. And I'm sorry for the late reply. Unfortunately, the solution isn't what I expected. My expected behavior is to get a 404 page display without changing the URL. For example, if you access any of the following URLs, the URL will not change and "This user does not exist" will be displayed. But the URL will change. This is not what I expected. http://we-are-sober.d3v-svr.com/users/11 http://we-are-sober.d3v-svr.com/users/999 – yhirochick Jul 19 '21 at 17:31
  • This is a small change to make, rather than redirecting return an error. I've updated my answer with the desired behavior and pushed the related code. – kissu Jul 20 '21 at 00:14
  • I'm sorry, but it's still not what I expected. I want to display a 404 page even on an ungenerated route like /users/999. – yhirochick Jul 21 '21 at 15:05
  • You will not be able to display the 404 page in a clean way if you want to stay on the `/users/999` path. You can meanwhile totally put a text/image or component at the place of the empty user info. The text variant is shown in my updated answer. Not sure about the difference between the code you have shared and what I've written (in my updated answer). – kissu Jul 21 '21 at 15:13

2 Answers2

3

My way of handling this kind of issue while minimizing the API calls required are following those steps:

  • generate a brand new Nuxt project
  • install axios: yarn add -D axios
  • add this to the nuxt.config.js file
import axios from 'axios'

export default {
  ...
  generate: {
    routes: async () => {
      const users = await axios.get('https://jsonplaceholder.typicode.com/users')
      return users.data.map((user) => ({
        route: `/users/${user.id}`,
        payload: user,
      }))
    },
    fallback: 'no-user.html', // this one is not needed anymore if you ditch the redirect!
  },
}

This will generate all the needed routes, while keeping the calls to a minimum thanks to payload that will be passed later on to the pages. More info can be found in the docs.

  • then, creating the /pages/users/_id.vue page does the trick
<template>
  <div>
    <div v-if="user">User name: {{ user.name }}</div>
    <div v-else-if="error">{{ error }}</div>
  </div>
</template>

<script>
export default {
  asyncData({ payload }) {
    if (payload && Object.entries(payload).length) return { user: payload }
    else return { error: 'This user does not exist' } // this will also catch users going to `/users/`
  },
}
</script>
  • create some no-user.vue page, error.vue layout and you should be gucci

At the end, we have 10 users from the mocked API. So those are the following cases:

  • if we go to /users/5, the user is already static so we do have it's info without any extra API call
  • if we go to /users/11, the user was not present at the time of build, hence he is not here and we are displaying an error
  • if we go to /users, we will still be sent to the /pages/users/_id page, but since the :id will be optional there, it will error and still display the error, an index.vue can of course handle this case

My github repo for this one can be found here: https://github.com/kissu/so-nuxt-generate-placeholder-users


This approach is called full static in Nuxt, as explained here: https://nuxtjs.org/announcements/going-full-static/

kissu
  • 40,416
  • 14
  • 65
  • 133
  • Thank you for answering. But my customer wants me to display the 404 page with the URL as it is, even if it is not generated. It may not be a pretty clean method, but I'll answer the method I found below. – yhirochick Jul 25 '21 at 04:59
1

It's a tricky way, but I've found the code that works as I want.

https://fes.d3v-svr.com/users/xxxxxx It's works that I expect.

  • User xxxxxx doesn't exist
  • Display 404 page
  • The URL /users/xxxxxx as it is

First, simply set .htaccess to rewrite non-exist page to 404 page

ErrorDocument 404 /no-user/index.html

Only above, Nuxt execute base on URL /users/xxxxxx/ and re-render the page as "UserXXXXXX" even he is not exist.

To avoid this, users/_id.vue is like bellow.

template

<template>
  <div v-if="ssr">User name: {{ user.name }}</div>
</template>

script

<script>
export default {
  asyncData({ payload }) {
    return { user: payload, ssr:true }
  },
}
</script>

It seems to be if a template is empty, nuxt will not execute the script depends on the URL.

It's very tricky, so I'll continue to how it is works.

kissu
  • 40,416
  • 14
  • 65
  • 133
yhirochick
  • 93
  • 1
  • 11
  • 1
    Rather than relying on `.htacess`, you could use a dynamic component in the `v-else` and display a `404` component. https://vuejs.org/v2/guide/components-dynamic-async.html#keep-alive-with-Dynamic-Components Also, saying "no" to a client is sometimes a good idea, especially if he is forcing a way that relies on an ugly hack – kissu Jul 25 '21 at 07:30