Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display server side validation error message?

We are building up a web application and we are having some issue in displaying server side validation error messages in frontend.

To give some context, we are running a single page application where frontend is implemented in react.js and backend api in asp.net. There are a number of forms in our application we ask users to fill in, and we plan to implement form validation both on client side and server side.

The work flow we are having now is to do client side validation first, and then pass the data to backend api. Backend then would first do the same validation frontend just did, to make sure that the request does not come from a malicious user who has bypassed clientside validations rules. For a request that comes from the normal channel, it should never fail this step and if it does, however, it simply means we are dealing with a hacker.

However, there are a bit more than that in server side validation. There are information which client side has no knowledge of, e.g. is the Bank Account user just typed exists in our database ? (we don't have a dedicated api to verify this, so we have to do this at the form level after user submitted the entire form to server side).

So in conclusion, there are two parts of server side validation rules: basic validation rules that is shared with frontend, and advanced validation rules that requires database access which is only available at server side.

Now the question is, how do we inform user after they fail these two type of validations?

Below are two proposals we have come about so far

Solution 1: use HTTP 400 - Bad Request to denote a business failure

we treat that 2 types of server side validation the same way and when any request fails any validation, we send back a HTTP 400 with a json object containing detailed error messages saying why it fails. A sample response would be

HTTP 400 Bad Request
{
      "bsb_name" :"bsb_name has special characters",          
      "bsb_number":"bsb number doesn't exist",
      "amount":"amount is required"
 }

Now the problem is, we are not the only party who can return HTTP 400. As we are using asp.net framework plus OWIN framework, the frameworks themselves can also send back HTTP 400 under a certain circumstances , and the HTTP 400 they send is quite different from us. This create extra work for frontend to differentiate 2 types of HTTP 400 and deal with them in different ways.

A proposal was made to to re-factor our response and instead of sending back JSON object, we send back plain string --- based on the assumption that asp.net framework/owin would send back their HTTP 400 in plain string (1). So at front end we don't have differentiate them and can handle both of them in the same way. Besides, to avoid introducing magic delimiters, we only return the first error message text rather than all of them. So a sample response could be

HTTP 400 Bad Request
"Bank account name has special characters" 

and we are going to treat it the same way as a HTTP 400 returned by the asp.net framework, say,

HTTP 400 Bad Request
Request header too long

So in both cases, a site user would see an error message after submitting their form saying "Bank account name has special characters" or "Request header too long"

solution 2 : Use Business Protocol rather HTTP Protocol

If the request fails the basic server side validation, which can only happen for hackers, server would throw exception immediately which would end up with a HTTP 500. Once receiving the HTTP 500, client side would simply direct user to a general error page saying something unexpected happened without any detailed information -- we try to avoid being hacking friendly.

If the request fails the advanced server side validation though, which can happen to a normal application user, server will still return a HTTP 200 - Success, but with a different status_code denoting that this is a error response. e.g.

  HTTP 200 Success
  {
       status_code: Err_123
       error_message: "bsb doesn't exist"
  }

I personally want to go with solution 2 but was told that we shouldn't use HTTP 200 for a validation error. As HTTP 200 stands for OK, using HTTP 200 in the case of server side validation failure doesn't make sense.

My understanding is, HTTP Status code is designed for HTTP protocol, and should have nothing to do with your business logic. A HTTP 200 only means client has sent the request to the server, and server has successfully handled it returned the response. Whether the response means transaction has been approved, or transaction has been rejected, or, the BSB_Number you submitted doesn't exists, it simply doesn't care. And it should NOT care.

So in the case where a transaction does get rejected, or the form has some invalid input that can only be detected at server side, we still should respond HTTP 200 but assign a different status_code, which is the equivalent of HTTP 400, BUT, it's in our business protocol. So effectively, we should distinct our business protocol from http protocol, and we can't use HTTP FAILURE to stand for a BUSINESS FAILURE(2).

Above is all my thought on this, and I welcome all comments, further discussion on this topic.

Cheers, Bo

  • (1): the assumption is wrong as I recently found out that IIS can return HTTP 400 under some circumstance. When that happens, it return a HTML page.

  • (2): An analogy could be

**IP -- TCP -- HTTP -- BUSINESS** If TCP layer fails, IP shouldn't be concerned; If HTTP layer fails, TCP shouldn't be concerned; If BUSINESS layer fails, HTTP shouldn't be concerned;

Isn't 'separate of concern' a common sense in programming?? I'm a bit surprised when seeing that somebody says "it's ok to return HTTP 400 in case of business failure"

like image 990
Bo Chen Avatar asked Oct 21 '16 01:10

Bo Chen


1 Answers

Separation of concerns ought to be first and foremost in my opinion.

Solution one, although stating the error originates in the business layer, implies that presentation is at fault. Now it's ambiguous as to where the error is actually originating.

On the other hand, solution 2 verbosely states that the HTTP transaction was successful and that the business logic responded with an error.

Ideally (in my opinion) all errors in the server side should be trapped and reported as an error in the business logic, so that the only time HTTP errors are thrown is when the user requests a bad or sensitive page.

This causes the system to fail more gracefully, and makes it more scaleable. (If you implement your API elsewhere for example, you don't need to include the HTTP error handling in it, only the display of said error)

As an aside, this might be better suited for P.SE and not SO.

like image 79
Enfyve Avatar answered Sep 28 '22 08:09

Enfyve