I am building a WebAPI as a learning project and attempting to use best practices. My first attempt is a authentication API, which takes an authentication object (JSON):
{
username: myusername,
password: mypassword
}
it calls my API on /api/authenticate as a POST, passing the object.
In my .Net code, I do some checks, and if the username/password pass, I create a jwt token, and return it with roles. My API returns a 200 with the token in the body (response in Chrome developer tools shows "ey.....", which is my jwt).
If I get an invalid username/password, I return a 401.
I'm not sure this is right. Should I rather return a 200 - and some other payload in the body? (Not sure what), and then should my successful login return JSON, such as:
{
success: true,
error: null
token: "ey.....",
}
A failed login return:
{
success: false,
error: null
token: null,
}
and then an error:
{
success: false,
error: 500
token: null,
}
And then client side code uses that to decide what to do? I'm trying to work on a best practice here to learn how to handle this in WebAPI.
I don't think there is really a "best practice" here. Some APIs return an error object like you did with JSON. That's completely fine. Other APIs return HTTP errors (401, 500, etc...). Other APIs return both. There are pros and cons to each method, so choose whatever you like or suits your needs best.
If you go with the first method, don't limit yourself to returning HTTP codes. Instead, return codes that give you and the consumers of your API more specific references to the errors. For example, code 401 doesn't tell me why the authentication failed. Probably that's fine for security, but I'm only using it here as an example, so instead you can return code 1001 for incorrect credentials, 1002 for an account that is locked, 1003 for an account that is pending approval, etc...
Pros of the first method: the API consumer can handle everything in the same code using a simple if...else or switch logic. It is also easier to test. The cons: you still need to use try...catch, because the request to the API may still fail, so the consumer code will have the above logic plus the try...catch logic.
Pros of the second method: it is more inline with the way we usually do things. Use try...catch to handle all errors, and the code inside will only be for the successful path. The cons: a little harder to test and you're stuck with the HTTP error codes.
The third method is a combination of the two. In some cases it is probably an overkill and adds some unnecessary complexity and repetition, but in other cases it can combine the benefits of the two worlds.
Here I give a different way of returning response message...
I hope, This will help you in returning the response message...
// For login success
In the below code, it shows the success response...
return Content(HttpStatusCode.Ok, error); This will helps to return the status code in the header of the Postman Tool.
if (result == null)
{
var error = new
{
Success = "true",
Token = "your token"
};
return Content(HttpStatusCode.Ok, error);
}
//for Unauthorized user login
In the below code, it shows the Unsuccess login response...
Here we can mention the error status to the user in response...
return Content(HttpStatusCode.Unauthorised, error); This will helps to return the status code in the header of the Postman Tool.
if (result == some condition)
{
var error = new
{
Error = new
{
StatusCode = HttpStatusCode.Unauthorised,
Message = "Invalid Credential...! ",
InternalMessage = "Some message"
}
};
return Content(HttpStatusCode.Unauthorised, error);
}
// for error
In the below code, it shows the Unsuccess login response...
Here we can mention the error status to the user in response...
return Content(HttpStatusCode.InternalServerError, error); This will helps to return the status code in the header of the Postman Tool.
if (result == somecondition)
{
var error = new
{
Error = new
{
StatusCode = HttpStatusCode.InternalServerError,
Message = "Error in functionality...!",
InternalMessage = "Some message"
}
};
return Content(HttpStatusCode.InternalServerError, error);
}
Related
I have the following .then example chain in my React Native client code, currently without a .catch because I am looking for advice on how to set it up:
await getUserInfo(userId, JSON.stringify(ratingsQueryRatingType), 1)
.then(async userRatingData => {
await findMatchHistory(userId, '', 3)
.then(async matchHistoryData => {
These functions make calls to my NodeJS server. The NodeJS server then sends back the data.
I am trying to find out how I can effectively send back an error from the server to the client, and have the .catch part in the client handle that (e.g. with Alert.alert(error)).
I tried to throw an error on my server as follows but then on my server I get Unhandled promise rejection. It appears that it does not send the error back to client.
// Other code before this part
if (response==='Success') {
return res.status(200).json({'status': 'success'})
} else {
throw 'Match record was not confirmed successfully'
}
Or is it common practice to send response objects from the server (instead of Errors) and then handling those on the client with some kind of if-statement, such as the following?
if (results['status']==='success') {
// Code
} else if (results['status']==='failure') {
// Code
}
I do read about .then chaining with .catch being an attractive option so it feels like this would not be the correct solution..
I think we should send error-codes to the client instead of sending message although you can do it too. you can check the status code based on the error occurred on the backend -> https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
Also, on client side you can use interceptor and create an error-handler service layer and based on the code you will be sending to the client side you can handle that. you can follow these steps to setup one for your app : https://bilot.group/articles/using-react-router-inside-axios-interceptors/
And, On the backend side if there is an error try logging it to log files on your server.
// Other code before this part
if (response==='Success') {
return res.status(200).json({'status': 'success'})
} else {
res.status(based on what happened on server).json({'status': 'failure'})
}
For .then channing instead you should use async/await. for better understanding how to use them and convert chain to asyn/await. This doc contains the step-by-step guide: https://advancedweb.hu/how-to-refactor-a-promise-chain-to-async-functions/
In my app ( i'm using next.js but it's more a general question ) i have a button that updates number of likes when clicked (+1). Here is relevant part of code:
const handleLikeClick = () => {
setNumberLikes(numberLikes + 1)
fetch('/api/updateLikes?origin=so-filter', {
method: 'POST'
})
}
And my API:
import { connectToDatabase } from '../../utils/mongodb'
export default async (req, res) => {
try {
const { db } = await connectToDatabase()
const { origin } = req.query
if (req.method === 'POST') {
await db.collection('likes').findOneAndUpdate({ page: origin }, { $inc: { likes: 1 }})
res.status(200)
}
}
catch (err) {
res.status(500)
}
}
I don't really care much if this POST request fails or not, therefore, i'm not checking for it and there is no additional logic if it actually fails. Is it a bad practice to do so ? Should i actually res.status(200).json({success:'updated'}) and .then my fetch request? Thank you.
Depends on what you want to achieve at the user level.
Although the result doesn't influence the flow of your program and doesn't break it, most of the times there is some importance to let the fetcher/user know what happened with the request.
Sometimes (like in your case) it can have an impact to the user experience. In your example, in case of failure, I think the user should get an error message or some sort of visualization that the like didn't cast, so he could try again or at least know that there was a problem.
(I'm pretty sure Facebook, Youtube, and StackOverflow just grays out upvoted or likes if something went wrong. In StackOverflow you even get a message with the specific error).
Edit
Code-wise it will work just fine since you are care to give a returned status code in any case (of success or failure).
From the documents:
The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.`
(Notice that you will want to handle network failures though).
I'm trying to log an error from a subscribe, but the error seems... incomplete?
I've never seen this kind of problem, nor could I find anything on Google. The code is the following:
this._http.post(this.urlPath, email).subscribe(
res => {
// stuff
},
err => {
console.log(err) // <- I need to log this
}
);
It works to an extent. When I open the browser's console, in order to check the error, what I get is this:
The thing is, it seems like there's missing information here. If I open the Network tab, the response for this same request looks like this:
As you can see, the real response has more information. I've tried using {observe: "response"}, but to no avail. I should note that if I try using fetch, the response comes complete, but I'd rather use HttpClient.
What is going on here?
When you receive a http error status code you can't access to the payload returned by the service by the same way that in a success case. Is like an special object.
But you can acccess to it doing some like this, using a pipe in your service and an error handler. This is a minimal example of it:
your.service.ts
...
handleError(error) {
return throwError(error.error);
}
return this.http.get ... the rest of your request.pipe(
catchError(this.handleError)
);
...
And where you consume your service, in err you can acces to full response that your error request contains.
...
, error => {
console.warn(error);
}
Or better than, you can throw the entire object to access to the error (response body) and the rest of params, like status code.
I am using async in nodeJS, and in my final callback I am handling the error and trying to send it back to my angular controller.
function (err, data) {
if (err) {
console.log(err);
res.status(500).send({ err : err});
}
else {
res.json({data: data});
}
});
Now the error in the console is.
[Error: Username is already in use]
I am not able to get this particular error in my angular controller I tried sending the error in all combinations such as .
res.status(500).send({ err : err[0]});
res.status(500).send({ err : err.Error});
This is what I get in my front end.
Object {data: Object, status: 500, config: Object, statusText: "Internal Server Error"}
config
:
Object
data
:
Object
err
:
Object
__proto__
:
Object
__proto__
:
Object
headers
:
(d)
status
:
500
statusText
:
"Internal Server Error"
How can I bring that username in use error to my front End.
500 errors are usually reserved for Server errors, and not for scenarios like the one you have described. Server errors should be handled by your server and elegantly presented to your front end. Client errors should be in the 400s. Why don't you try a 409 or a 400:
res.status(409).json({error: "Username is already taken"});
Look at HTTP Status codes for more:
409 Conflict
The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.
Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can't complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.
Note: Also, as a good practice, make sure you return your res. functions, for predictable flow of control, like so:
return res.status(409).json({error: "Username is already taken"});
In a Parse custom webhook, which is of the form:
app.post('/receiveSMS', function(req, res) {
Where receiveSMS is hooked up to the Twilio api and this method is properly called (I have logs to prove it), but I'm trying to query on my tables within this method and it doesn't seem to be working.
Is this allowed, or is there anything special I need to do to make this work?
var contactObj = Parse.Object.extend("Contact");
var contactQuery = new Parse.Query(contactObj);
console.log(req.body.From);
contactQuery.each(function(contact) {
and the body of the each call never gets called.
Is this allowed, and if so, what am I doing wrong here?
Update -- The entirety of the webhook code block is:
app.post('/receiveSMS', function(req, res) {
console.log('receive SMS');
console.log(req.body.Body);
res.send('Success');
if(req.body.Body.toLowerCase() == "in" || req.body.Body.toLowerCase() == "out") {
twilio.sendSMS({
From: "(xxx) xxx-xxxx",
To: req.body.From,
Body: "It's been noted, and notifications have been sent. Check us out!"
}, {
success: function(httpResponse) {
console.log(httpResponse);
response.success("SMS Sent!");
},
error: function(httpResponse) {
console.error(httpResponse);
response.error("Uh OH, something went wrong");
}
});
if(req.body.Body.toLowerCase() == "in") {
console.log("in was received");
// eventQuery
var contactObj = Parse.Object.extend("Contact");
var contactQuery = new Parse.Query(contactObj);
console.log(req.body.From);
// contactQuery.equalTo("phone", req.body.From);
contactQuery.first({
success: function(contact) {
console.log("found contact");
console.log(contact);
}, error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
}
}
});
This code is called and the logs "console.log('receive SMS')" and the like are all called, except for what is inside the query's first call.
Queries on tables is fine, but you can't use the each() function, as that is restricted to only work in background jobs.
You'll have to use find() or first() or get() depending on your needs.
UPDATE
OK, after seeing your full code I have some ideas as to why it isn't working. First off you're sending res.send("Success"); before you're finished, I'm not positive but I think this causes it to stop running the rest of your code (haven't checked, could be wrong).
Also you're doing multiple async operations without chaining them so the contactQuery.first() will run before the twilio.sendSMS() is finished.
Inside twilio.sendSMS() you're calling response.success() / response.error(). These are for cloud methods, not web hooks, so I expect these would be throwing errors server-side (check the logs on the Dashboard).
Inside contactQuery.first() you are using alert() which isn't supported in cloud code.
I'm not sure if those mistakes will be caught early and throw errors or if they'll raise run-time exceptions, but they should be fixed, your code re-deployed and try again. Then report any errors in the server logs.
Yes, it's allowed, I'm using the same web hooks.
My guess is that you probably have defined security restriction on your Contact class that prevent the query to fetch anything. What's the security setting on this class ?
You can either try to relax the constrains, or login as a dummy user, and execute the query (approach that I chose).
cheers
-A