Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

REST in advance

5.00/5 (6 votes)
15 Mar 2017Apache5 min read 9.3K  
On the client side, I want to get and display the appropriated (for example: Email or pwd is incorrect) when login fail. How can I do in REST?

Where can I find the basic of REST?

For the basic of REST, Please visit:

On the client side, I want to get and display the appropriated (for example: Email or pwd is incorrect) when login fail. How can I do in REST?

Please add this method into UsersController as below:

[HttpPost]
[Route("login")]
[ValidationActionFilter()]
public IHttpActionResult Login(LoginRequest request) {
	/*Verify username and pwd and throw exception if invalid*/
	throw new ValidationException("users.login.invalidUserNameOrPwd");
}

In this code, we force the API always  returns "users.login.invalidUserNameOrPwd" validation error to client.

Wen make the call to "<root api>/users/login":

And the response as below:

 I expect to receive explicit error message (such as: Email or pwd is incorrect), but the Api return "users.login.invalidUserNameOrPwd" as key of exception. You may misunderstand my idea, is that correct?

no, I understand your expectation.

In this case, we should avoid sending explicit error message to the end user as API can serve for multiple type of devices, and multiple languages.

So API return the key, and client side will translate that key to appropriate message on their device in appropriated language. This let api focus on the application business rather than spending time on translate the message key to appropriated language of current user.

There are someone on the internet told that, we should use 401 (Unauthorized) if login fail. Should we use i n this cases?

No, should not please.

401 (Unauthorized) should be used in the case that client want to get user profile (restricted resource) without appropriated credential.
Yes, we will return 401(Unauthorized) in this case.
In our case, user did not provide enough information for Login action.

Ok, So, which error code (Http Status Code) that I should use in this case?

I prefer to use Bad Request (400), as this HTTP Status Code was intend to be used in the case that request does not provide enough information for expected action on server.

I see, What is about the role of "ValidationActionFilter" above Login function?

This is attribute in .net. Look loser at the code of "ValidationActionFilter" attribute, we have:

public class ValidationActionFilter : ActionFilterAttribute
{
	public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
	{
		if (actionExecutedContext.Exception == null) {
			return;
		}
		if (actionExecutedContext.Exception is ValidationException) {
			actionExecutedContext.Response = actionExecutedContext.Request
				 .CreateResponse(HttpStatusCode.BadRequest, ((ValidationException)actionExecutedContext.Exception).Errors);
		}
	}
}

In this code, We override "OnActionExecuted" method. This will be called after the function was executed.

And in this function, we check if the result of appropriated function throw any ValidationException exception (using actionExecutedContext.Exception is ValidationException). Then create appropriated response and return to caller/ client with Bad Request (400).

I think we can try/catch in Login function and handle exception case as expected.

Yes, we can. But the try/catch block will be duplicated in all function. That was not good.

How many type of error/ exception we usually have in the application?

From my view, We usually have 2 types:

System error/ exception

Application error/ exception.

We usually handle in different way for those type of errors/ exceptions.

Can you explain more detail about system error?

They are errors related to network, infrastructure, ....

It means that the caller request was not reached to your application yet in this case. Sych as: Not Found (404), .....

For those case, we can display an error page or showing the error message to user. and this is the same for all cases.

So, What was about application error?

In different view, this was related to the logic of the application. when caller send request to server with invalid data for appropriated logic. For example: Login with empty email/username.

In this case, we need to let caller know about these errors and it can display appropriate error on login form. Most validation errors are application error/exception.

To make it clearer, Can you show me example of those 2 type of errors when user login into system?

Sure, Let see, Users enter their email/pwd into login form and send request to server with "<api root>/users/login" URI.

If that URI ("<api root>/users/login") was not exist on server. it means that server side was not configured to handle this type of request. So .Net Framework can not map your request to appropriated action on server. This error is system error as your application code was not reached in this case.

Otherwise, the URI exists and the Login method of UsersController was called. Then we do the validation the request and found that email is empty but it was required in this case. and throw exception. 

This is application error in this case, as the error was thrown by the application due to un-satisfied data.

How can caller know that they receive system error when calling my API?

For those system errors, the server usually response with specified Http status (for example: 404: Not found, ...). So caller can base on this to know which type of error they received. 

How about application error/ exception?

For application error, We should return response with BadRequest (400) and attach more detail of error in body of response as photo below:

and body of response:

OK, Can you show me how to implement this  in webapi?

Please look at the simple code below:

public HttpResponseMessage Login(LoginRequest request) {
	try
	{
		/*Verify username and pwd and throw exception if invalid*/
		throw new ValidationException("users.login.invalidUserNameOrPwd");
	}
	catch (ValidationException ex) {
		return Request.CreateResponse(HttpStatusCode.BadRequest, ex.Errors);
	}
}

I see that error/data was returned differently in the case of fail/success. This require the client side need to check carefully before handling the response from server. Is there any other solution for this?

This is common issue for most REST api.

For avoiding this, We need to wrap the response result. So the response on both cases (fail/success) will be in the same format.

Look at the code below:

[HttpPost]
[Route("login")]
public HttpResponseMessage Login(LoginRequest request)
{
	ResponseData<bool> response = new ResponseData<bool>();
	try
	{
		/*Assume that we will throw exception in the case client send request ="exception"*/
		if (request != null && request.Email == "exception")
		{
			throw new ValidationException("users.login.invalidUserNameOrPwd");
		}
		else {
			response.SetData(true);
		}
	}
	catch (ValidationException ex)
	{
		response.SetError(ex.Errors);
	}
	return Request.CreateResponse(response.HasError() ? HttpStatusCode.OK : HttpStatusCode.BadGateway, response);
}

 In this code, we always return the ResponseData object to client side, it was json format and we know how many fields it has.

In success case, the errors field in response data is null/empty, so we can get data from "data" field and perform appropriated action, as below:

Otherwise, in the fail case. Just get the list of errors in "errors" field and notify to end user:

How can we distinguish system error and application error?

We can do this based on "Http Status Code".

In case of exception/ error when making the call to server, check if we have the "errors" field in response.

  • If YES, it is application error. Just get the list of errors from "errors" field and perform appropriated action.
  • If NO, this is system error

I see, we have try/catch block in each function for handling exception case, As mentioned in this article, we can remove this by using "ValidationActionFilter", is that correct?

Yes, that is correct.

 Thank for reading.

Note: Please like and share to your friends if you think this is usefull article, I really appreciate

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0