Handling REST API server response - http-status-codes

I've been working with REST API CodeIgniter for more than a year and usually when I want to return any response I will return 200 for all kind of request. I know that there are status code provided for all response but I am actually quite wondering, is it wrong if I use 200 for all response? And determine the data status with true and false.
Here is the sample code that I always use. Let's say to check whether the user is exist or not.
CodeIgniter REST API
$user = [ 'id' => 1, 'name' => 'John Doe' ];
$this->response([
'status' => !empty($user) ? true : false,
'user' => $user
], REST_Controller::HTTP_OK);
React Native
try {
const res = await axios.get('https://www.example.com/retrieve-user?user_id=3');
if (res.status == 200){
if(res.data.status == true){
// user found
} else {
// user not found
}
} else {
alert('Internal server error.')
}
} catch (err) {
console.error(err);
}
Based on the example, I am actually depending on the status code 200 to determine if there is an error in the server (code error, invalid request, etc).
So my question is, is it okay to stick with this method?

Given your API, yes handling code 200 seems enough as your API might not return any other HttpCode.
Given a bigger API (even simple), no.
Your API might return a 204/404 if no user if found with given id.
Your API might return a 503 if your API is under deployment (and unavailable) or under maintenance, and you may retry 30 seconds later.
Your API might reject request if a given header is missing (Content-Type ...) and return a 400...
EDIT 1 :
if (res.status == 200){
// user found
} else if (res.status == 204) {
// user not found
} else {
alert('An error occured')
}

Related

Alert not showing when I do a passed response

$bd = Carbon\Carbon::now()->diffInYears(Carbon\Carbon::parse(request()->input('form.birthdate');
if ($bd <= 6){
return response(['message' => "That's less than 6, not allowed",500]);
}
and in my axios request
}).then(response => {
if (response.status === 500 ){
alert(error.response.data.message);
}
else {
window.location.replace("/admin/users/"+this.user.id);
}
I don't know why my alert not showing there. iT sucks?
How i handle axios is below
First return your response from laravel like this. (Include the status code)
return response(['message' => "That's less than 6, not allowed"], 400);
After that on your axios side get the message like this
axios.post().then(function(response){
// this will run if the status code is 200 in laravel response
// Get passed data like 'response.data.message', You will get your passed values after 'response.data'
}).catch(function(error){
notify('error', error.response.data.message); // You will get your passed values after 'error.response.data'
})
The catch function will run when your ajax response is 400 500 etc etc
Modify your response as follows:
return response(['message' => "That's less than 6, not allowed"], 500);
Parmeters for the response helper are response(string message, int status_code, array? headers)
If you wan't return json use
return response()->json(array json, int status_code, array? headers)
For more informations see the documentation https://laravel.com/docs/7.x/helpers#method-response

Response from laravel is not logging properly in vue js

I am working on a project. Where I am using laravel as back end and VueJS as front end. I called an API that requests laravel to insert a user. I want to check whether the back end validation is successfully done or not. for this, I'm sending an email that is already in the database. the validation response is all okay. But I want to log a message only instead of logging the whole response. somehow the message is not logging but the response is logging. I could not figure out what the problem is
API call
Response
I want to log the message, not the whole response
Chain the ```.catch(error => { console.log("Errors, error") }); on your ajax request.
I have written this small helper once. It can handle Laravel and also your custom messages returned from the backend. It's not perfect but can get your working in a good direction.
export function errorMessage(error) {
let errorResponse = error.response.data.errors ? error.response.data.errors : error.response.data;
let errors = [];
for (let key in errorResponse){
if(key === 'message'){
errors.push(errorResponse[key]);
}
else if (key !== 'exception' && key !== 'file' && key !== 'line' && key !== 'trace'){
for (let i = 0; i < errorResponse[key].length; i++){
errors.push(errorResponse[key][i]);
}
}
}
return errors;
}
You can use it in your catch block like let errors = errorMessage(error)). I fed my notification plugin with messages that displays on the top of the screen.
So the 422 is an error you will have to handle it in a .catch block
.then() // everything went good
.catch() // something went wrong
Something like this
.then(response => {
// great
})
.catch(error => {
if (error.response.status === 422) {
// do something here
}
}

Sending appropriate error responses on web actions

I have some web-enabled actions that are exposed through API Connect in IBM Cloud Serverless Functions.
Some of my actions use request-promises to call external REST services and I need to be able to catch an error and respond with an appropriate status-code to the caller.
Since the actions are web-enabled, the documentation indicates that I can use an annotated JSON to set the headers, status-code and body of the response. But it seems that, seems the API expects to always get a Content-Type=application/json, the response processor is failing to understand my annotations in the case of an error.
I tried the following without success:
let rp = require('request-promise');
function main(params){
//setup options
return rp(options).then(
res => {
return res;
}
).catch(
err => {
return { error: { statusCode:err.statusCode } }
}
);
}
Another variation:
let rp = require('request-promise');
function main(params){
//setup options
return rp(options).then(
res => {
return res;
}
).catch(
err => {
return { statusCode:err.statusCode }
}
);
}
The problem is that the status-code I always get is 200... I also tried to change the runtime to node8.0 without success.
Thanks!
I found the answer myself :)
In order to get the status-code and headers, one must set the field Response Content Type to `Use "Content-Type" header from action", while setting up the mapping between the API call and the action....

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.

Nodejs - A better way to handle ajax request to the server

Now I ran into a problem with managing ajax requests on nodeJS server. Currently I have this system it works, but it's ugly and not that efficient.
router.post('/get', function(req, res) {
var request = req.body.request;
if (request == null) {
res.json({success: 'false', error: 'Response returned null'});
return;
}
if (request == "render_page") {
var page = req.body.page;
if (page == null) {
res.json({success: 'false', error: 'Page returned null'});
return;
}
res.render(page);
} else if (request == "render_article") {
var temp = {
articles: [
{title: 'ABC', text: 'This is a test article'},
{title: 'XYZ', text: 'Just another random article'}
]
};
res.render('template/article-format', temp);
} else {
res.json({success: 'false', error: "Unknown request " + request});
}
Is there a better way to do this and even maybe make it dynamic? Also the server likes to crash if something goes wrong so there's that.
You seem to be fighting with the concepts of GET and POST. GET requests are supposed to be used for fetching things (like pages). Yet, you have specified a POST request, then named it /get, and then put the context in the request body.
If you simply leveraged some parameters in your GET requests, then you don't need to send a post with body (which I'm assuming you are using a POST request because you thought you needed to be able to send the request context data, in this case the page name).
So, you have a bunch of get requests that are being called as post requests. Really what you want is something like this:
router.get('/page/:page', function(req, res) {
var page = req.params.page;
// Logic here
});
And for handling the "null" page, you just route them to the /page url automatically (since if there is no parameter, it is just the /page url).
For further reading, I'd look over:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
http://expressjs.com/4x/api.html#req

Resources