Service failing to correctly return status code to UI - spring

Maybe this is silly question but I'm trying to learn Spring MVC and I have everything working except for the exceptions. So I have a simple form application where the user can register, if the user already exists I'd like to send an error code to the UI so that it knows why it failed. Heres my code:
#ResponseBody
#PostMapping("users")
public ResponseEntity addUser(#RequestBody User user) {
List<User> users = usersService.addUser(user);
if(users == null) return new ResponseEntity(HttpStatus.EXPECTATION_FAILED);
else return new ResponseEntity<>(users, HttpStatus.ACCEPTED);
}
It works fine, as in it returns a status code to the UI but the exception returns it in this string format:
Error: Request failed with status code 417
at createError (createError.js:17)
at settle (settle.js:19)
at XMLHttpRequest.handleLoad (xhr.js:69)
The log above is from a console log from the UI, right after the catch below:
function register(user) {
return dispatch => {
axios.post(`${BASE_URL}/users`, user).then((response) => {
console.log(response);
dispatch(resetError());
dispatch(success(user));
}).catch((e) => {
console.log('e', e);
dispatch(error(e.status));
})
};
function success(user) { return { type: userConstants.REGISTER, payload: user } };
};
Funny enough it actually prints exactly what I'm looking for if the http call succeeds. Here's what it prints on the happy path of the promise (ACCEPTED):
Notice that it has a status property. I'd very much not like to parse a string on the UI side just to get the error code from the service. Why is the response object different? The only thing I've changed is the status code. How can I make the error status give the UI a nice object instead of a string?
If you'd like to pull the branch here is the URL: https://github.com/MatTaNg/react-form
The code snippets are in the UsersResource file

Instead of console.log('e', e) try console.log('e', e.response.status).
Source:
https://github.com/axios/axios#handling-errors

Related

(VueJS, Axios) Different way to catch errors

I'm currently building a single page application based on Laravel and VueJS.
Is there any better way then mine to handle errors with axios?
This is how I currently do it when a user clicks on login button:
VueTemplae:
methods : {
authenticateUser() {
axios.post('/api/login', this.form).then(() => {
this.$router.push({name : 'home'});
}).catch((error) => {
this.error = error.response.data.message;
});
}
}
Api route:
public function login() {
try {
// do validation
} catch(Exception) {
// validation failed
throw new Exception('login.failed');
}
// manually authentication
if(Auth::attempt(request()->only('email', 'password'))) {
return response()->json(Auth::user(), 200);
}
// something else went wrong
throw new Exception('login.failed');
}
Unfortunately, throwing an exception always prints an internal server error into the console.
If I return something else than an exception, axios always executes then().
Is there any way to prevent this or a better way to handle axios responses?
Thank you!
Your API needs to return a response with a 4XX status code in order for the catch block to fire in your Vue component.
Example:
After you catch the error on the API side, send a response with status code 400 Bad Request. It will be formatted similarly to your successful login response, but with an error message and 400 status code instead of 200.

Angular 8 patch request httpclient.subscribe() doesnt hit response, err or ()

I am encountering an issue I do not understand.
Inside one service I have the following code.
when the code hits PATCH LINE, it jumps immediately to RETURN NOTHING LINE.
the catchError line is not hit.
the () line is not hit
the err line is not hit.
I have compared this to working services and I do not see any difference.
patchItem(item_: Project): Observable<Project> {
const url: string = `${this.serviceUrl}/${item_.id}`;
const data = JSON.stringify(item_);
console.log('inside patch item');
console.log(url);
this.http.patch(url, data, httpOptions) //PATCH LINE
.pipe(
catchError(err => this.handleError('PatchItem', err))
)
.subscribe((response) => {
console.log('item patched ');
return this.myResponse.push(response);
}
, err => console.log("inline error encountered")
,() => console.log("this seems to have completed")
);
console.log("return nothing"); //RETURN NOTHING LINE
return null;
}
The API is C# webapi 2
It is being hit.
There is a problem though, I am expecting the JSON to be passed in and webForm_ is always NULL.
This is probably an important clue.
Again, i compare this to working calls and can not find a difference.
When I inspect the variable in jquery, it has the correct value.
In postman, i get the expected response.
[System.Web.Http.HttpPatch]
[Route("{itemId_}")]
public IHttpActionResult PatchById([FromUri] int itemId_, [FromBody] Models.mpgProject webForm_)
{
return JSONStringResultExtension.JSONString(this, "foobar for now", HttpStatusCode.OK);
}
To save a cycle, here is the handleError function.
I have copy/pasted this from a service that is working as expected.
protected handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error); // log to console instead
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
How can Angular be skipping all the subscription stuff when it IS actually calling the API?
I think it has to be a simple syntax issue, but I have been staring at it for hours... :-(
Happy to post any other code requested...
tyia
ps - I would also be happy to retitle the question. I am at a loss for how to phrase a good question title for this...
Your handleError method returns a function (that returns an Observable) when it should return an Observable.
It looks as if the error handler you copied from another service does not fit here.
I could imagine an error handler like this:
private handleError<T>(operation = "operation", error: HttpErrorResponse) {
console.error(error); // log to console instead
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of({} as T);
}
This method returns an Observable of an empty object. However, this might not be the best option to react on a failing HTTP PATCH request. You would better throw that error up to the component and let the user retry.

How to detect response in VueJS?

I ask the help of knowledgeable people
im create a RESTfull API project on Vue.js (Vuex also)
And im get small problem
The server to which I am sending the request is down why how idn
Can someone tell me how can im detect this message from response
This response dont have any massege, error, status, statusText, text, preview and response
All this field is empty
If someone have expirience about this or some info I will be very grateful for that
You can do something like this to handle these cases:
submitRequest() {
axios.post('/api/test', this.testData)
.then(response => {
// handle success
})
.catch(function(error) {
// handle error
if (error.response) {
// The request was made and the server responded with a status code
} else if (error.request) {
// YOU CAN HANDLE IT HERE
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
}
});
}

The browser overwrites the content of the message after receiving the response

I have a little problem with DingoAPI and Vue.js when I'm trying to get my error message from response. I think the browser is replacing my custom message by the default one. Here is my code:
PHP script
if($request->readerId){
Return succes (this works properly)
else{
return $this->response->error(
'No reader',
400 //(or diffrent code)
);
}
Vue.js script
await axios.post(API_URL + 'card/', {
some data
}, {
headers: {
headers
},
}).then(({data}) => {
context.commit(SET_RESPONSE, data);
}).catch((error) => {
console.log(error.message);
throw error
})
When I'm trying to look on my message in the network tab I can see (so DingoAPI did it correctly):
{"message":"No reader","status_code":400}
But when I'm using console.log(error.message) or trying to show it on the page there is standard error message:
Request failed with status code 400
Is there a way to set error message with DingoAPI and catch it in my .js script?
Maybe I need to write my own custom exception?
What you want is access to the data of the response from your error variable.
console.log(error.response.data.message); // No reader
Otherwise you can log error.response to see the object:
console.log(error.response);
If you wonder why it's printing Request failed with status code 400:
The problem is when the console.log tries to output the error, the string representation is printed, not the object structure, so you do not see the .response property.
Source: https://github.com/axios/axios/issues/960#issuecomment-309287911

Axios Reponse Interceptor : unable to handle an expired refresh_token (401)

I have the following interceptor on my axios reponse :
window.axios.interceptors.response.use(
response => {
return response;
},
error => {
let errorResponse = error.response;
if (errorResponse.status === 401 && errorResponse.config && !errorResponse.config.__isRetryRequest) {
return this._getAuthToken()
.then(response => {
this.setToken(response.data.access_token, response.data.refresh_token);
errorResponse.config.__isRetryRequest = true;
errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
return window.axios(errorResponse.config);
}).catch(error => {
return Promise.reject(error);
});
}
return Promise.reject(error);
}
);
The _getAuthToken method is :
_getAuthToken() {
if (!this.authTokenRequest) {
this.authTokenRequest = window.axios.post('/api/refresh_token', {
'refresh_token': localStorage.getItem('refresh_token')
});
this.authTokenRequest.then(response => {
this.authTokenRequest = null;
}).catch(error => {
this.authTokenRequest = null;
});
}
return this.authTokenRequest;
}
The code is heavily inspired by https://github.com/axios/axios/issues/266#issuecomment-335420598.
Summary : when the user makes a call to the API and if his access_token has expired (a 401 code is returned by the API) the app calls the /api/refresh_token endpoint to get a new access_token. If the refresh_token is still valid when making this call, everything works fine : I get a new access_token and a new refresh_token and the initial API call requested by the user is made again and returned correctly.
The problem occurs when the refresh_token has also expired.
In that case, the call to /api/refresh_token returns a 401 and nothing happens. I tried several things but I'm unable to detect that in order to redirect the user to the login page of the app.
I found that in that case the if (!this.authTokenRequest) statement inside the _getAuthToken method returns a pending Promise that is never resolved. I don't understand why this is a Promise. In my opinion it should be null...
I'm a newbie with Promises so I may be missing something !
Thanks for any help !
EDIT :
I may have found a way much simpler to handle this : use axios.interceptors.response.eject() to disable the interceptor when I call the /api/refresh_token endpoint, and re-enable it after.
The code :
createAxiosResponseInterceptor() {
this.axiosResponseInterceptor = window.axios.interceptors.response.use(
response => {
return response;
},
error => {
let errorResponse = error.response;
if (errorResponse.status === 401) {
window.axios.interceptors.response.eject(this.axiosResponseInterceptor);
return window.axios.post('/api/refresh_token', {
'refresh_token': this._getToken('refresh_token')
}).then(response => {
this.setToken(response.data.access_token, response.data.refresh_token);
errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
this.createAxiosResponseInterceptor();
return window.axios(errorResponse.config);
}).catch(error => {
this.destroyToken();
this.createAxiosResponseInterceptor();
this.router.push('/login');
return Promise.reject(error);
});
}
return Promise.reject(error);
}
);
},
Does it looks good or bad ? Any advice or comment appreciated.
Your last solution looks not bad. I would come up with the similar implementation as you if I were in the same situation.
I found that in that case the if (!this.authTokenRequest) statement inside the _getAuthToken method returns a pending Promise that is never resolved. I don't understand why this is a Promise. In my opinion it should be null...
That's because this.authTokenRequest in the code was just assigned the Promise created from window.axios.post. Promise is an object handling kind of lazy evaluation, so the process you implement in then is not executed until the Promise was resolved.
JavaScript provides us with Promise object as kind of asynchronous event handlers which enables us to implement process as then chain which is going to be executed in respond with the result of asynchronous result. HTTP requests are always inpredictable, because HTTP request sometimes consumes much more time we expect, and also sometimes not. Promise is always used when we use HTTP request in order to handle the asynchronous response of it with event handlers.
In ES2015 syntax, you can implement functions with async/await syntax to hanle Promise objects as it looks synchronous.

Resources