Vue/Nuxt.js: How to handle 404 on nested routes (nuxt-child) - vue.js

I am using nested routes from nuxt.js:
https://nuxtjs.org/guide/routing#nested-routes
with a child component:
https://nuxtjs.org/api/components-nuxt-child/
To make it easer to understand:
I have an events page and a nested postView component.
Left you can see a calendar view which stays even if the route changes.
To the right I have the postdetails, which are replaced (nuxt child component):
With Nuxt this folder/file setup will setup my router correctly.
This works so far, but now I want to handle the case if somebody enters a non-existent postSlug in the url.
So far, it would just not be displayed, but I want to have a proper 404 code.
Do I have to set this manually? (I am currently making an axios call from the calendarview to see if the post.json exists. If so, I load it into the vuex store)
…
// if it is not in store get it via axios
else {
const ax = axios.create({
baseURL: `${window.location.origin}/events`
})
ax.get(`event.${this.$route.params.postSlug}.json`)
.then((response) => {
const newActivePost = response.data && response.data.items ? response.data.items.find(p => p.slug === this.$route.params.postSlug) : false
if (newActivePost) {
this.activePost = newActivePost
this.$store.dispatch('data/saveCompletePosts', this.activePost)
}
})
.catch((error) => {
console.log(error.response.data)
console.log(error.response.status)
console.log(error.response.headers)
})
}
But with this I would never get an error. Even if the json file does not exist. I always get a 200 status code.
I am not sure why this is the case, but maybe the response just contains the page without the post itself. Actually the same if I call the url with the missing post. If I have a look at the response string:
<body data-n-head="">
<div data-server-rendered="true" id="__nuxt">
<div class="nuxt-progress" style="width:0%;height:2px;background-color:#000000;opacity:0;"></div>
<div id="__layout">
<div class="Layout">
<!---->
<div class="SkipNavigationLink">Springe direkt zum Hauptinhalt</div>
<div class="no-ssr-placeholder"></div>
<main id="main" class="Layout__content">
<!---->
</main>
<div class="Footer">
<div class="Footer__logo-wrapper">
<div class="Logo"><img src="/_nuxt/img/logo-dark.0b2ed1f.svg" alt="Logo Image" class="Logo__image"></div>
</div>
You can see that there is the layout and the footer but no content.
My guess would be that if I could return a proper 404 status code in the nested route/child component I would also get a 404 in my axios response.
One – maybe hacky idea – would be to manually set the status code:
ax.get(`event.${this.$route.params.postSlug}.json`)
.then((response) => {
const newActivePost = response.data && response.data.items ? response.data.items.find(p => p.slug === this.$route.params.postSlug) : false
if (newActivePost) {
this.activePost = newActivePost
this.$store.dispatch('data/saveCompletePosts', this.activePost)
} else {
// SET STATUS CODE MANUALLY?
response.request.status = 404
}
})
And then handle the thing differently.
But I don't know if that is a good idea...
Some help about nuxt routing/404 handling is much appreciated.
cheers
-- - - - - - - Edit
I just found this
https://nuxtjs.org/api/pages-validate
I think this would actually call a 404 but I would not know how to validate, because there is no pattern in the postSlug.
I really would have to check with all the json files of the posts...
-- - - - - - Edit 2:
I can't even do that:
TypeError: Cannot assign to read only property 'status' of object '#<XMLHttpRequest>'
🤷‍♂️

I think you are looking for error function from context
async asynData({ params, store, error }) {
try {
await axioscall here
} catch (e) {
error({ statusCode: 404, message: 'Page not found' })
}
},

Not sure if it is related, I had redirect to error page working in top level pages directory (pages/_.vue), something like this:
async asyncData(context) {
...
try {
const response = await context.$axios(options);
return {
isPostLoaded: true,
loadedPost: response.data.data.postBy
}
} catch (error) {
context.error({ statusCode: 404, message: 'Page not found' });
}
}
but it didn't work in nested directory in pages (pages/posts/_slug/index.vue)
adding this seem to have helped, I checked if response data was available (might not be the best way but seem to work)
if (response.data.data.postBy === null) {
throw new Error();
}
...
async asyncData(context) {
...
try {
const response = await context.$axios(options);
if (response.data.data.postBy === null) {
throw new Error();
}
return {
isPostLoaded: true,
loadedPost: response.data.data.postBy
}
} catch (error) {
context.error({ statusCode: 404, message: 'Page not found' });
}
}

Related

What are the best practices for handling vuex errors?

I'm new to vue. I use interceptors for handling action responses, all easy with successful responses. But I would like to know what are the best practice to handle error responses.
I want to show a toastr with error message from response by default if there's no catch block in the action, but if there is a catch, do only catch function with no toastr shown.
Also, is it ok to handle unauthorized response making a redirect to login page directly in interceptor and what advices can be given about it?
My current interceptor looks like this:
axios.interceptors.response.use(
(response) => {
return response.data.data;
},
(error: AxiosError) => {
const data = error.response?.data;
const code = data?.code;
if (code === ErrorCodes.NEED_EMAIL_CONFIRMATION) {
router.push("email-verification").then();
} else if (code === ErrorCodes.UNAUTHORIZED) {
router.push("sign-in").then();
} else {
if (undefined !== data.error) {
toaster.error(data.error);
} else {
toaster.error(i18n.t("unexpected"));
}
}
return error;
}
);
but I don't like too many responsibilities here and I don't know how to avoid toastr show when the action has a catch function
You can control error toast notification from where you send the request, by sending an extra config.
Using axios:
axios.post('/api-name', data, {
config: {
showToast: true,
},
})
and then on axios intercept:
axios.interceptors.response.use(
response => {...},
error => {
const showTost= error.config.errorToast
if(showToast){
// show toast you can pass custom message too...<3
}
}

Error page not working on production mode on Nuxt.js

I am developing an application with Nuxt in which I consume an API with dynamic query parameters.
The website where the application is deployed only contains the query parameters if it is opened from another application.
I want to redirect to an error page if the site url does not contain the query parameters to generate the json that the application consumes.
I dont't undestand why the error page works as intended in development mode, but when I use the app in production mode the error page is not displayed.
Am i missing something? or is there a better way to redirect to an error page if the API does not contain the query parameters?
this is my index.vue from where I do the fetch to the dynamic API:
async created() {
// GET request using fetch with async/await
let uuidBooking = this.$route.query.uuidBooking;
let nombreCliente = this.$route.query.nombreCliente;
const response = await fetch(
`https://e7pmpg7z85.execute-api.us-west-2.amazonaws.com/prod/obtienedatosbooking?uuidBooking=${uuidBooking}&nombreCliente=${nombreCliente}`
)
const data = await response.json();
console.log((JSON.parse(data.JsonBooking)));
this.bookingData = JSON.parse(data.JsonBooking);
if (response.uuidBooking === this.$route.query.uuidBooking && response.nombreCliente === this.$route.query.nombreCliente) {
this.response = response
} else {
// set status code on server and
if (process.server) {
this.$nuxt.context.response.statusCode = 404
}
// use throw new Error()
throw new Error('Booking not found')
// this.$nuxt.error({ message: 'Error test'})
}
},
I have also tried this with try and catch on the index.vue:
async created() {
// GET request using fetch with async/await
let uuidBooking = this.$route.query.uuidBooking;
let nombreCliente = this.$route.query.nombreCliente;
try {
const response = await fetch(
`https://e7pmpg7z85.execute-api.us-west-2.amazonaws.com/prod/obtienedatosbooking?uuidBooking=${uuidBooking}&nombreCliente=${nombreCliente}`
)
const data = await response.json();
console.log((JSON.parse(data.JsonBooking)));
this.bookingData = JSON.parse(data.JsonBooking);
} catch (error) {
context.error(error)
}
}
this is my error page:
<template>
<b-container class="error-container d-flex flex-column align-items-center justify-content-center">
<div class="text-center p-3">
<h1 v-if="error.statusCode === 404">Page not found</h1>
<h1 v-else>We couldn't find any information for this request.</h1>
Get in touch: help#exikhan.com.mx
<!-- <NuxtLink to="/">Home page</NuxtLink> -->
</div>
</b-container>
</template>
<script>
export default {
props: ["error"],
// layout: "blog", // you can set a custom layout for the error page
};
</script>

To print 'Not Connected' only when the API returns 'null' string with 401 UNAUTHORISED in Vuejs

I am new to Vuejs and I need some help in order to solve this one.
What I am excepting is whenever my API returns like image below i.e. null with 401 UNAUTHORISED STATUS, I want to display NOT CONNECTED. And whenever my API returns 201 STATUS OK, I want to display CONNECTED. I really don't have any idea.
Here is my API response:
Below i am sharing my code:
<template>
<div class="api_data">
<span class="trick_not_ok" v-if="errorMsg" >Not connected</span>
<span class="trick_ok" v-if = "noerror" >Connected</span>
</div>
</template>
<script>
export default {
name: Api_data,
data () {
return {
Value: [],
errorMsg: '',
noerror: ''
}
},
created() {
this.$http.get('https://my_api_goes_here/doubt/')
.then((response) => { this.Value = response.data })
.catch((error) => {
if(error.response.status === 401){
this.errorMsg = ''
}
else{
this.noError = ''
}
console.log(error)
})
}
}
</script>
If I do it this way, nothing is displayed on my page. And in console I am getting Failed to load resource: the server responded with a status of 401 (UNAUTHORIZED). Please someone help me with this by sending me the modified code.
You can try to achieve the desired result rewriting some logic from your code.
In this code, I just changed 3 things:
the template condition from if.
<span class="trick_ok" v-if="errorMsg !== ''" >Not connected</span>
Added new variable in data(), it is the same used in template
errorMsg: ''
changed catch to receive a value in case of error (catch clause)
The resulting code would be something like:
<template>
<div class="api_data">
<span class="trick_ok" v-if="errorMsg !== ''" >Not connected</span>
<span v-if="connected !== ''" >Connected</span>
</div>
</template>
<script>
export default {
name: Api_data,
data () {
return {
Value: [],
errorMsg: '',
connected: '',
}
},
created() {
this.$http.get('https://my_api_goes_here/doubt/')
.then((response) => {
this.Value = response.data
this.connected = 'ok'
this.errorMsg = ''
})
.catch((error) => {
console.log(error)
this.connected = ''
this.errorMsg = 'Detail: ' +error
})
}
}
</script>
This code is not tested. But you can have an idea.
The main objective is show message not connected if there is any error in request.
There is some considerations you have to check the status code in the catch function, because if you receive an error like 403 or 500 in the way I implemented it still will show the message not connected and perhaps it is not what you want.
So in catch clause maybe you need to check for the status code number too.
.catch((error) => {
//if (status === 401) {
// this.errorMsg = 'Detail: ' +error
//}
})
I left this code commented because you should figure out a way to get the status code from the error from the parameter in catch clause.
if your response look like these on
{
"success": false,
"message": "Unauthorized! Access Token was expired!",
"error_code": 500
}
so, in vue2 ( vue.js )
.catch(err => {
err.response.data.message // here to access the message json
})

Access raw error object in Nuxt error page

The Nuxt.js error page, as described here, is passed an error property which can be used to figure out what the error was and display something appropriate.
However, the error object passed looks something like:
{
message:"Cannot read property 'nonexistentThing' of undefined",
statusCode:"TypeError"
}
I would like to take action on the error page (hitting an error-reporting API) which ideally would have access to the underlying error object including the backtrace etc.
Is there a way to either
access this from the error page; or
make a plugin which intercepts the error while Nuxt’s own error-handling is in flight?
Although I didn't found a way to access the underlying error object I've found this error method from nuxt where you can throw your exception which finally gets displayed in Nuxt's error page. You can access this error method in the context object (https://nuxtjs.org/api/context/)
An example error including the exception could be this:
export default {
asyncData ({ params, error }) {
return axios.get(`https://my-api/posts/${params.id}`)
.then((res) => {
return { title: res.data.title }
})
.catch((e) => {
error({ statusCode: 404, message: 'Post not found', myError: e })
})
}
}
So then you could process the exception in the error page like this
<template>
<div class="container">
<h1 v-if="error.statusCode === 404">Page not found</h1>
<h1 v-else>An error occurred</h1>
<nuxt-link to="/">Home page</nuxt-link>
</div>
</template>
<script>
export default {
props: ['error'],
mounted() {
const { statusCode, message, myError } = this.error;
// send myError to error-reporting API
}
}
</script>

Use nuxt.js dynamic routing to pass parameters

I am going to query the database to return the result after receiving a parameter in the dynamic route, and find that the console reports an error.
TypeError: Cannot read property 'title' of null
When I went to see the request, I found that the first request returned the data, and then sent the same request but the spliced parameter was null and reported the error.
This is the second request 304
This is my page code.
`
<templat>
<div class="wrapper qa-content">
<div class="qa-title">
<div class="fl title">
<h2>{{problem.title}}</h2>
<p>
<span
>{{labes(index)}}</span>
<span>{{timeago(problem.createtime)}}</span>
</p>
</div>
import "~/assets/css/page-sj-qa-detail.css";
import axios from "axios";
import problemApi from "#/api/problem";
import replyApi from "#/api/reply";
import labelApi from "#/api/label";
export default {
asyncData({ params }) {
return axios
.all([
problemApi.findById(params.id),
replyApi.findByProId(params.id),
problemApi.findPL(params.id)
])
.then(
axios.spread(function(pojo, replyList, labelList) {
return {
problemId: params.id,
replyList: replyList.data.data,
problem: pojo.data.data,
labelList: labelList.data.data
};
})
);
},
data() {
return {
CurrentreplyId: "",
commentList: [],
labelName: [],
textarea: "",
dialogVisible: false,
content: "",
editorOption: {
// some quill options
modules: {
toolbar: [
[{ size: ["small", false, "large"] }],
["bold", "italic"],
[{ list: "ordered" }, { list: "bullet" }],
["link", "image"],
["blockquote", "code-block"]
]
}
}
};
},
mounted() {
console.log("app init, my quill insrance object is:", this.myQuillEditor);
},
methods: {
labes(index) {
console.log(this.labelList);
labelApi.findOne(this.labelList[index].labelid).then(res => {
this.labelName.push(res.data.data.labelname);
console.log(this.labelName);
});
},
check(id) {
console.log(id);
replyApi.findByParentid(id).then(res => {
this.commentList = res.data.data;
});
},
shows(item) {
console.log(item.id);
if (item.content === null || item.content === "" || item.content === "") {
return false;
} else {
return true;
}
}
`
This page is dynamically routed from the previous page.
<nuxt-link :to="'/qa/items/'+item.id" target="_blank">{{item.title}}</nuxt-link>
Ok. I think that helps. If I understand you correctly you do the following:
You are on a previous page
You click on the nuxt-link with the item id in the route
Your new route loads and you get an error: Because your page fetches the data from the API twice (but you did not reload the page) is that correct?
If so, I am not sure why your asyncData is executed twice, but you could probably solve it like this:
asyncData({ params }) {
if (params.id) {
return axios
.all([...
}
This would make sure that your request is only made ifan ID is present and it would not send null.
Honestly I don't know why the request is resent...
I also don't really understand how your API looks like. I see that it somehow returns promises, and that you can call methods on it... Something I haven't seen in such a context, but OK :).
Besides that you seem to execute further API calls in your methods.
Maybe that is the problem:
<span>{{labes(index)}}</span>
I don't see what would be passed into this method. This index is not defined...
When then calling the
labes() (did you mean labels?) method, you execute
labelApi.findOne(this.labelList[index].labelid)
but as index is undefined, I think this.labelList[index] will not return something useful and you make a request there?
(Depending on what your api.findOne() method does. Could it be that itself sends a request to an actual remote API?)
Cheers