I am using Restangular to consume a REST API and it always gives me this error even though I can see the XHR request being made successfully on the developer console.
Error
{
"data":null,
"status":-1,
"config":{
"method":"GET",
"transformRequest":[null],
"transformResponse":[null],
"jsonpCallbackParam":"callback",
"headers":{"Accept":"application/json, text/plain, */*"},
"url":"http://localhost:8080/profile/v1/support/tickets"},
"statusText":""
}
Restangular API call
angular.module('adf-widget-tickets-module-service',['restangular'])
.service('ticketCRUDService', function ($rootScope, Restangular, $http) {
Restangular.setBaseUrl('http://localhost:8080/profile/v1/support');
this.readTickets = function () {
Restangular.all('tickets').getList().then(function (response) {
var test = response.json;
console.log(test);
return test;
},function (err) {
console.error(JSON.stringify(err));
},function () {
console.log("loading......");
})
};
}
Could you please tell me what am I doing wrong here?
Update
Here is the code for my REST endpoint
#GET
#Path("tickets")
#Produces("application/json")
public Response getAllTickets(){
ArrayList<Ticket> allTickets = new ArrayList<>();
try {
allTickets = elasticAPI.getAllTickets();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return Response.ok(gson.toJson(allTickets), MediaType.APPLICATION_JSON_TYPE).build();
}
why your endpoint is returning a -1 status code?
Angular resolves only between: 200 and 299
/*
* A response status code between 200 and 299 is considered a success status and will result in
* the success callback being called. Any response status code outside of that range is
* considered an error status and will result in the error callback being called.
* Also, status codes less than -1 are normalized to zero. -1 usually means the request was
* aborted, e.g. using a `config.timeout`.
* Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning
* that the outcome (success or error) will be determined by the final response status code.
*/
https://github.com/angular/angular.js/blob/master/src/ng/http.js#L457
As I guessed the problem was not on my client side code. It was because of that the request being rejected from the server due to CORS. Setting proper response headers for CORS support fixed the problem.
Related
In my backend I implemented an IpRateLimit middleware with the AspNetCoreRateLimit package in my .net core entity framework backend. When an IP Address x makes y calls in a specific time, it gets blocked for a certain time and the backend should return an 429 error and this works fine when testing with postman. But when the I make an request with axios, that is supposed to get blocked because of the ip rate limiter, I receive an axios error:
"Access to XMLHttpRequest at 'https://localhost:44372/api/Users/Login/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
"POST https://localhost:44372/api/Users/Login/ net::ERR_FAILED"
After receiving this error, I have added the needed header, but it hasn't changed the result. Other axios requests to my backend (also post, put and delete) work fine, but when the ip rate limiter hits, I just get the cors error.
I implemented the limiter in my application as in the following tutorial:
https://edi.wang/post/2019/6/16/ip-rate-limit-for-aspnet-core
React axios request:
async function buildPostAndFetch(url, param, header) {
const finalurl = `${BASE_URL}${url}`;
return axios.post(finalurl, param, {headers:{"Access-Control-Allow-Origin": "*"}})
.then(res => {
response(res);
return res.data ? res.data : true;
})
.catch(err => {
handleError(err);
return false;
})
}
handleError() {
const handleError = err => {
setError(true);
if(err.request?.status === 0) {
console.log("*********************************************")
console.log(err.request);
console.log("*********************************************")
// throw new Error("API is currently offline or you are not connected to the internet:(");
} else if(err.response.status === 429) {
console.log("*********************************************")
console.log(err.response);
console.log("*********************************************")
}
}
}
When requestion and limiter hits I always get in the err.request.status === 0 path.
Most server systems/runtimes by default don’t add application-set headers to 4xx and 5xx responses but instead only add them to 2xx success responses and maybe to 3xx redirects.
So you may need to do explicit config to force headers to get added to 4xx responses, in order for that 429 response end up with the Access-Control-Allow-Origin header.
In Apache and nginx for example, that’s done by adding the always keyword to the header-setting directive. Maybe your server system has some similar thing.
You get a CORS error because that 429 error has no Access-Control-Allow-Origin header.
Firstly, ensure that you have Install-Package Microsoft.AspNetCore.Cors from NuGet.
Then, please add the following to your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Put it before AddMvc() if there's any
services.AddCors();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Put it before UseMvc() if there's any
app.UseCors(
options => options
.WithOrigins("http://localhost:3000")
.AllowAnyHeader()
.AllowAnyMethod()
);
app.UseMvc();
}
After finish the above configuration, by inspecting on your network tab (Browser's developer tools) you may see the one of the returned response header is access-control-allow-origin : http://localhost:3000.
Try:
app.UseCors(opt => opt
.WithOrigins("http://localhost:3000"")
.AllowAnyHeader()
.AllowAnyMethod());
The protocol (http/https) can not be omitted. Also, the cors middleware should be placed after app.UseRouting but before UseAuthorization.
You can see Middleware Order .
I am trying to add RETRY Logic in the context of - I make an API call -> response is 401 -> I invoke APi to request for a NEW Token in the background. The poin there si MY API Calls shouldnt fail. Following is my API File (This is common - Every API in my application invokes this File to make an FETCH)
NOTE : I have seen articles using the fetch().then() approach, but we are using YIELD.
Specific API File -
// apiRequest = part of api.js file i am specifying below
const response = yield retry(3,1000,apiRequest,options); // My apiRequest while trying for getting new access tokens send me a NULL, do we want that ?
if (undefined !== response && null !== response) {
const formattedResponse = yield apply(response, response.json);
if (response.status === 200) {
yield call(handleAddCampaignResponseSuccess, formattedResponse);
} else {
yield call(handleAddCampaignResponseFailure, formattedResponse);
}
} else{
// Show some Message on UI or redirect to logout
}
// api.js
function* apiRequest(options) {
const { method, body, url } = options;
const accessToken = yield select(selectors.AccessToken);
const idToken = yield select(selectors.IdToken);
try {
var response = yield call(fetch, url, {
method: method,
body: body,
headers: {
"Content-Type": ContentTypes.JSON,
Authorization:
accessToken != "" ? `Bearer ${accessToken} ${idToken}` : "",
},
});
if (null !== response) {
if (response.status === HTTP_CODES.HTTP_UNAUTHORIZED) {
// Unauthorized requests - redirect to LOGOUT
// Request for Refresh Token !
yield put(refreshTokenOnExpiry());
return null; // Is this necessary
} else if (response.status === HTTP_CODES.HTTP_NOT_FOUND) {
return null;
} else if (response.status === HTTP_CODES.HTTP_SERVER_ERROR) {
// Logout cos of serrver error
yield put(handleLogout());
return null;
} else {
console.log("From Else part");
// - Called on intent to ensure we have RESET redirections and that it does not cause issues of redirection.
yield put(resetRedirections());
return response;
}
} else {
// Handle Logout
yield put(stopTransition());
yield put(handleLogout());
}
} catch (error) {
// Cors Error in case of DEV URL
// See if SAGA is Still listening to the Action Dispatches
console.log("From CATCH BLOCK");
yield put(stopTransition());
yield put(handleLogout());
return null;
}
}
My concern is the documentation says that - if API request fails then it will retry, I do not get the meaning of it. Does it mean if the API returns NULL, or anything other than Http 200 ? Cos I want the API to retry in case of 401
API.JS is the file invoked by ALL API's across my website. Also, how can I ensure that refreshTokenOnExpiry gets called ONLY once (meaning at a time there will be multiple API calls and each one when got a 401 will eventually invoke refreshTokenOnExpiry this API)
I am new to generator functions, so I am sure I must have goofed up somewhere.
Also if anyone who can help me build this code correctly, would be great help. Thanks !
Adding Image for reference - I want the FAILED API's to be retried which aint happening :
My concern is the documentation says that - if API request fails then it will retry, I do not get the meaning of it. Does it mean if the API returns NULL, or anything other than Http 200 ? Cos I want the API to retry in case of 401
Scroll down to the section "Retrying XHR calls" in the redux-saga recipes to get an idea of what the retry effect is doing behind the scenes.
The retry effect can be used on any function, no just an API call, so it's not looking at the response code. It defines "failure" as code that throws an error rather than completing execution. So what you need to do is throw an error in you apiRequest.
No guarantees, but try this:
if (response.status === HTTP_CODES.HTTP_UNAUTHORIZED) {
// Unauthorized requests - redirect to LOGOUT
// Request for Refresh Token !
yield put(refreshTokenOnExpiry());
throw new Error("invalid token");
}
You need to figure out how to make sure than the new token gets set before retrying. You might want to build your own chain of actions rather than relying on retry. For example, you can put an action with type "RETRY_WITH_NEW_TOKEN" that has a payload containing the original options and the token that it was tried with. That way you can compare it against the token in state to see if you have a new one.
this is related to this question:
Handling unauthorized request in react-query
I understand the point that React-Query doesnt care about responses codes because there is no error. So for example if the server respond with a 400 "Bad Request", do i have to check for this on the data returned by the muate function?
const handleFormSubmit = async (credentials) => {
const data = await mutateLogin(credentials);
// Do i have to check this data if for example i wanna show an error message
// "Invalid Credentials"?
};
I need to save the user on the cache.
const useMutateLogin = () => {
return useMutation(doLogin, {
throwOnError: true,
onSuccess: data => // Do i have to check here again if i receive the user or 400 code
})
}
Thanks.
react-query does not take care of the requests and it is completely agnostic of what you use to make them as long you have a Promise. From the documentation we have the following specification for the query function:
Must return a promise that will either resolves data or throws an error.
So if you need to fail on specific status codes, you should handle that in the query function.
The confusion comes because popular libraries usually take care of that for you. For example, axios and jQuery.ajax() will throw an error/reject if the HTTP status code falls out of the range of 2xx. If you use the Fetch API (like the discussion in the link you posted), the API won't reject on HTTP error status.
Your first code snippet:
const handleFormSubmit = async (credentials) => {
const data = await mutateLogin(credentials);
};
The content of data depends on the mutateLogin function implementation. If you are using axios, the promise will reject to any HTTP status code that falls out of the range of 2xx. If you use the Fetch API you need to check the status and throw the error or react-query will cache the whole response as received.
Your second code snippet:
const useMutateLogin = () => {
return useMutation(doLogin, {
throwOnError: true,
onSuccess: data => // Do i have to check here again if i receive the user or 400 code
})
}
Here we have the same case as before. It depends on doLogin implementation.
I'm currently trying to POST data to my aws lambda functions triggered by aws api-gateway using the aws-amplify react lib.
Here is the code :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(console.log(err))
In the main case, everything is OK.
But my lambda function is design to validate the input data and return a status code 400 with a returned payload looking like that :
{
"errors": [
{
"field": "title",
"message": "This field is required"
}
]
}
I would like to catch those errors in order to display them in the frontend but aws-amplify seems to have an undocumented behavior.
By default, status code 400 returned are throw with a default error message :
Error: Request failed with status code 400
at createError (createError.js:16)
at settle (settle.js:18)
at XMLHttpRequest.handleLoad (xhr.js:77)
Is there a way to get the returned payload instead of this magical error?
It turns out that under the hood, aws-amplifyuse Axios to make http calls.
When using Axios, you have to console.log(error.response): https://github.com/axios/axios/issues/960
Here is the fix I've made :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(error => console.log(error.response.data))
A Pull Request on the aws-amplify documentation is open : https://github.com/aws/aws-amplify/pull/633
I also faced the similar issues, It showed the default error message "Request failed with status code 400", instead of the message that is returned from API.
I logged the Error object and it did not show the response attribute in it. But we do have response attribute. I tried logging the Error.response and it did contain the response sent from the API.
Just figured out this by going through the 'Cancel API requests' Amplify docs.
From what I can see this is the contents of the error object returned by the API call:
Heres what I am doing to just print out the error, obviously you would do a lot more here but its a good start.
async uploadUser(state, payload) {
const promise = API.graphql({
query: createUser,
variables: { input: payload },
});
try {
await promise;
} catch (error) {
// Print out the actual error given back to us.
console.log(error.errors[0].message);
// If the error is because the request was cancelled we can confirm here.
if (API.isCancel(error)) {
// handle user cancellation logic.
console.log(error.message);
}
}
Hope that helps 😃
I want to send mail to user i succeeded for that using spring boot and angular but i have a problem is that in my console always show me the message that my mail: succeesfuly sent: as mail is sent to user:
this is my angular js controller.js:
$scope.sentMail = function() { var data = new FormData();var mail= $scope.account.mail;
console.log('test 1 :', $scope.account.mail);
console.log("sent mail");
$http.post(_contextPath + '/sentEmail',mail).then(Failure)
.catch(Success);
function Success(success) {
// console.log(response.data);
console.log(' email is successfully sent:', $scope.account.mail);
}
function Failure(error) {
console.log('A problem occurred while sending an email.');
}
}
this is my controller.java:
#RequestMapping(value = "/sentEmail", method = RequestMethod.POST, headers = "Accept=application/json")
public ModelAndView processForgotPasswordForm(ModelAndView modelAndView, HttpServletRequest request, #RequestBody String mail) throws MessagingException
{
System.out.println("print");
User optional= usersService.findByEmail(mail);
if((optional== null))
{
modelAndView.addObject("errorMessage", "We didn't find an account for that e-mail address.");
}
else{
optional.setResetToken(UUID.randomUUID().toString());
usersService.save(optional);
String appUrl = request.getScheme() + "://" + request.getServerName();
SimpleMailMessage passwordResetEmail = new SimpleMailMessage();
passwordResetEmail.setFrom("test#test.com");
passwordResetEmail.setTo(optional.getUserEmail());
passwordResetEmail.setSubject("Password Reset Request");
passwordResetEmail.setText("Hello "+ optional.getUserFirstname()+" "+ optional.getUserLastname()+", \n \n You recently request to reset your password for rotana platform. Please click the link below to reset it : \n \n"+ appUrl +":8080/neorchaWEBPlatform"+ "/reset?token=" + optional.getResetToken()
+"\n \n if you did not request a password reset, please ignore email or reply to let us know.\n \n Thanks, \n \n Rotana Group \n \n P.S. We also love hearing from you and helping you with any issues you have. Please reply to this email if you want to ask a question.");
modelAndView.addObject("ResetMessage", "An e-mail has been sent to " + optional.getUserEmail());
emailService.sendEmail(passwordResetEmail);
}
modelAndView.setViewName("forgotPassword");
return modelAndView;
}
any help,thanks in advance.
.then will handle 'successful' responses. .catch will handle any errors in your success or error functions. You don't have an error function registered. I believe your code should be setup something like this:
$http.post(_contextPath + '/sentEmail',mail).then(Success, Failure)
.catch(UnexpectedError);
function Success(success) {
// console.log(response.data);
console.log(' email is successfully sent:', $scope.account.mail);
}
function Failure(error) {
console.log('A problem occurred while sending an email.');
}
function UnexpectedError(error) {
console.log('Something unexpected happened in Success() or Failure()');
}
The success and error callbacks look at the status being returned by your server. From the Angular documentation:
A response status code between 200 and 299 is considered a success
status and will result in the success callback being called. Any
response status code outside of that range is considered an error
status and will result in the error callback being called. Also,
status codes less than -1 are normalized to zero. -1 usually means the
request was aborted, e.g. using a config.timeout. Note that if the
response is a redirect, XMLHttpRequest will transparently follow it,
meaning that the outcome (success or error) will be determined by the
final response status code.
see: https://docs.angularjs.org/api/ng/service/$http