How can I get the actual error from fetch api - asp.net-core-mvc

I am making a fetch api call but in case of a 500 error the following middleware kicks in and sends back a json object in response body.
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
if (context.Response.HasStarted)
{
throw;
}
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
var json = JToken.FromObject(ex);
await context.Response.WriteAsync(json.ToString());
}
});
On the client side I have the following code
return fetch(url, content)
.then(function(res) {
if (!res.ok) {
console.log(res, res.json())
throw Error(res.statusText);
}
return res;
})
.then(res => res.json())
.catch(e => console.log('Error fetching accounts:', e))
I am not able to access the json with error information. How can I do it ?
Working code after following the correct answer
return fetch(url, content)
.then(function(response) {
if (!response.ok) {
return response.json()
.then(function(obj) {
throw Error(obj.ErrorMessage)
})
}
else {
return response.json()
.then(json => {
/*further processing */
})
}
}).catch(/* work with the error */)

The json function of the Response object returns a Promise, not the actual parsed value.
res.json()
.then(function(object) {
// Here you have the parsed JSON object.
console.log(object);
});

Related

Apollo Client GraphQL: When getting FORBIDDEN error, automatically get new JWT AccessToken and RefreshToken. How does the logic work?

In the following code, you can see that I am creating an errorLink. It makes use of an observable, a subscriber and then it uses this forward() function.
Can someone explain to me what's exactly happening here. I am bit familiar with observables, but I cannot understand what's going on here.
When creating the observable, where does the observer argument come from?
I would love to dive a bit deeper.
Also, why is bind used, when creating the subscriber?
const errorLink = onError(
({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
for (let err of graphQLErrors) {
switch (err.extensions.code) {
case "FORBIDDEN":
console.log("errs!")
// ignore 401 error for a refresh request
if (operation.operationName === "RehydrateTokens") return
const observable = new Observable<FetchResult<Record<string, any>>>(
(observer) => {
console.log(observer)
// used an annonymous function for using an async function
;(async () => {
try {
console.log("yop bin hier")
const accessToken = await refreshToken()
console.log("AT!", accessToken)
if (!accessToken) {
throw new GraphQLError("Empty AccessToken")
}
// Retry the failed request
const subscriber = {
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
}
forward(operation).subscribe(subscriber)
} catch (err) {
observer.error(err)
}
})()
}
)
return observable
}
}
}
if (networkError) console.log(`[Network error]: ${networkError}`)
}
)
Just so that you are understanding the context.
Iam combining mutliple apollo links.
const httpLink = createHttpLink({
uri: "http://localhost:3000/graphql",
})
// Returns accesstoken if opoeration is not a refresh token request
function returnTokenDependingOnOperation(operation: GraphQLRequest) {
if (isRefreshRequest(operation)) {
return localStorage.getItem("refreshToken")
} else return localStorage.getItem("accessToken")
}
const authLink = setContext((operation, { headers }) => {
let token = returnTokenDependingOnOperation(operation)
console.log("tk!!!", token)
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
}
})
const client = new ApolloClient({
link: ApolloLink.from([errorLink, authLink, httpLink]),
cache: new InMemoryCache(),
})

How can I get the error status codes from a Vuex response?

I am trying to handle errors in my Laravel/Vue application. As far as I can tell, I have everything in place as I am seeing what I expect. However, I am unable to get/return the status code if the response is anything other than 200.
console.log('status: ', response.status); // 200
If the response is 400 (or anything other than 200), I am unable to read it.
console.log('status: ', response.status); // undefined
Here is what I am creating for a response in my Controller:
Controller.php
...
if ($exception->getCode() === 400) {
return response()->json(['errors' =>
[
'title' => 'Bad Request',
'detail' => 'The username or password you have entered is invalid',
],
], $exception->getCode());
}
In the network tab, the response is coming back as 400, and the response object looks like this:
{"errors":{"title":"Bad Request","detail":"The username or password you have entered is invalid"}}
Awesome!
The request in my Vue component looks like this (click event handler):
...
try {
await this.$store.dispatch("user/signInWithEmailAndPassword", this.form)
.then(response => {
console.log('status: ', response.status);
switch (response.status) {
case 200:
console.log('good to go!');
break;
case 400:
console.log('400 error'); // not getting here
break;
case 401:
console.log('401 error'); // or here
break;
default:
console.log('some other error'); // end up here all the time
break;
}
})
.catch(error => {
console.log('SignInForm.authenticate error: ', error);
});
} catch (error) {
console.log("SignInForm.handleSubmit catch error:", error);
}
In my Vuex store, I am just returning the response from my service to see what I'm getting:
Vuex Store.vue
return await UserService.signInWithEmailAndPassword(credentials)
.then(response => {
return response;
...
UserService.vue
...
return await client.post('/v1/login', credentials)
.then(response => {
return response;
})
.catch(function (error) {
console.log('UserService.signInWithEmailAndPassword error: ', error); // getting here
return error;
});
So far, the only luck I'm having is seeing this in my console:
UserService.signInWithEmailAndPassword error: Error: Request failed with status code 400
How can I read the 400 error code to show the error I really want? It seems all the pieces are there. I'm not handling the error response correctly. Thank you for any suggestions!
EDIT
I have updated my code to reflect your suggestions, and I believe it stems from not returning my UserService call correctly. Here's what I'm doing now:
return await client.post('/v1/login', credentials)
.then(user => {
console.log('user: ', user);
return user;
})
.catch(function (error) {
console.log('error: ', error);
return error;
});
When I provide an invalid un/pw, I am getting into the catch, and seeing the error message which is:
error: Error: Request failed with status code 400
I am failing to properly return it though. As in, the code that calls this method is always ending up in the .then and not in the .catch block.
I feel like this is a really simple thing that I'm making incredibly difficult. Thank you for your help!
then is called when the the status is: status >= 200 && status < 300. Errors are catched in the catch method. The above code should be changed like this:
try {
await this.$store.dispatch("user/signInWithEmailAndPassword", this.form)
.then(response => {
console.log('status: ', response.status);
console.log('good to go!');
})
.catch(error => {
switch (error.response.status) {
case 400:
console.log('400 error'); // not getting here
break;
case 401:
console.log('401 error'); // or here
break;
default:
console.log('some other error'); // end up here all the time
break;
}
console.log('SignInForm.authenticate error: ', error);
});
} catch (error) {
console.log("SignInForm.handleSubmit catch error:", error);
}
Of course you can destruct the error and get the response directly
.catch(({ response }) => {
switch (response.status) {
UPDATE
You should also properly return the error from the service:
return await client.post('/v1/login', credentials)
.then(user => {
console.log('user: ', user);
return user;
})
.catch(function (error) {
console.log('error: ', error);
return Promise.reject(error);
});
You could add a const variable in your js getting the content of a file which returns an array of key->value and then accessing it from your js with the error message.
Example of config file:
return [
"200" => "Todo bien",
"201" => "Todo mal"
];
Vue file:
showItems()
{
axios.get('shopping-cart').then(response => {
if ( response.status != 200 ) return this.error = this.errors[response.status];
this.items = response.data;
}).catch(error => this.error = error);
}

How to get response from other file request response in Laravue

I'm trying to get the response from File2.vue and pass back to File1.vue. I'm using VueJS v1.0.28, How to get response from other file request response?
File1.vue
File2.updateNotice(this,{'id':id}).then((response) => {
console.log("response from File2 :",response)
});
File2.vue
updateNotice(context,params) {
let url = staff_service.STAFF_NOTICES_READ_RESOURCE;
Vue.http.post(
url,
params
).then(response => {
return response;
})
}
Error response from File1:
Cannot read property 'then' of undefined
This is now solved, added a
return
before
Vue.http.post(
in File2.vue
File1.vue
File2.updateNotice(this,{'id':id}).then((response) => {
console.log("response from File2 :",response)
});
File2.vue
updateNotice(context,params) {
let url = staff_service.STAFF_NOTICES_READ_RESOURCE;
return Vue.http.post(
url,
params
).then(response => {
return response;
})
}

Sails.JS Socket in Overridden Actions of the Controller

I've overridden an action in the controller that was generated from a blueprint API in Sails.JS.
I used the create action. Now I can't get a Sails.JS socket event from that action anymore, however, other actions are working fine.
io.socket.on('posts', function gotHelloMessage(data) {
console.log('Post!', data);
});
io.socket.get('/posts', function gotResponse(body, response) {
console.log('Posts: ', body);
})
What's the way to implement so that the create action also generates the event with my newly implemented actions?
create: function(req, res) {
if (
!_.has(req.body, "title") ||
!_.has(req.body, "body") ||
!_.has(req.body, "category")
) {
return res.serverError("No field should be empty.");
}
var uploadPath = "../../assets/posts";
return req
.file("thumbnail")
.upload({ dirname: uploadPath }, async function(err, uploadedFiles) {
if (err) return res.serverError(err);
let post;
try {
post = await Posts.create({
title: req.body.title,
body: req.body.body,
category: req.body.category,
thumbnail:
uploadedFiles.length === 0
? ""
: uploadedFiles[0].fd.split("/").reverse()[0]
}).fetch();
return res.json({ result: post });
} catch (err) {
return res.json({ error: err });
}
});
}

How to turn on Autorization?

"By default, an admin-on-rest app doesn’t require authentication".
I have written an application with AOR and Loopback API, etc, and it works well. Except for one thing, I can't turn on turn on authentication. Any username/password will work, just like in the Demo.
From what I can see all required components load, AuthClient etc., Loopback is configured and is waiting for user authorization requests but never gets any.
I copy/pasted a lot of Demo's parts...
Any hints please?
I use the unchanged authClient from kimkha aor loopback
import storage from './storage';
export const authClient = (loginApiUrl, noAccessPage = '/login') => {
return (type, params) => {
if (type === 'AUTH_LOGIN') {
const request = new Request(loginApiUrl, {
method: 'POST',
body: JSON.stringify(params),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ ttl, ...data }) => {
storage.save('lbtoken', data, ttl);
});
}
if (type === 'AUTH_LOGOUT') {
storage.remove('lbtoken');
return Promise.resolve();
}
if (type === 'AUTH_ERROR') {
const { status } = params;
if (status === 401 || status === 403) {
storage.remove('lbtoken');
return Promise.reject();
}
return Promise.resolve();
}
if (type === 'AUTH_CHECK') {
const token = storage.load('lbtoken');
if (token && token.id) {
return Promise.resolve();
} else {
storage.remove('lbtoken');
return Promise.reject({ redirectTo: noAccessPage });
}
}
return Promise.reject('Unkown method');
};
};

Resources