How should backend application respond to error or invalid input? - java

We are creating an app for iOS and have chosen Java running on Google App Engine for the backend. We use Google Cloud Endpoints to generate client library for iOS. We are relatively new to most of this and we are not quite sure how the backend should respond after an error based on user action. For example we have a method for creating a custom user, which should return the user-object if the save was successful. But there may be conditions that are not met and if so the app must get an appropriate response and handle present to the user. This is how we thought of doing it:
#ApiMethod(name = "createUser", httpMethod = HttpMethod.POST)
public User createUser(User user) throws BadRequestException
{
if(!validateUsername(user.getUsername()))
throw new BadRequestException("Invalid characters in username");
//Do stuff and save user
.........
return user
}
And then maybe throw a custom exception derived from ServiceException if the username exists etc. Is this a reasonable way to do it? I just feel like it's a little brutal to be throwing exceptions every time something like this happens. It also also appears in the log which could make it difficult to distinguish between actual application errors that needs to be fixed.

I believe your approach for throwing error 400 (BadRequestException) is fine in this case.
Additionally, Endpoints also provides other exceptions like UnauthorizedException (401), ForbiddenException(403), etc. Check out the list.
You can also have custom Exceptions that you can map to other HTTP codes that Endpoints supports. So your approach to have a Custom Exception is fine but do keep in mind that it will need to map to one of the HTTP codes eventually.
When it comes to parsing out the response in the client and doing some custom processing based on the error, it could get trickly, since all the control that you have to set the specific details are limited to the message attribute. So you will need to hack out the message format in such a way that you can parse and figure out what to do.

Related

what should be returned for http put request if id does not exist?

I am building the restful web service. For the put request, I first find the testBean with the id in the pathvariable. If it does not exist, then I create a new one. I am wondering if it is right to create a new one here, or I should throw the exception. Because id is auto increment, if I create a new TestBean, the id saved in the db is different from the one from the url path parameter.
#PutMapping("/Test/{id}")
public TestBean updateTestBean(#PathVariable long id, #RequestBody TestBean newTestBean) {
return testBeanService.getTestById(id)
.map(testBean -> {
testBean.setBRR(newTestBean.getBRR());
testBean.setModifiedDate(newTestBean.getModifiedDate());
return crewsBeanService.saveTestBean(testBean);
})
.orElseGet(() -> {
newTestBean.setId(id);
return testBeanService.saveTestBean(newTestBean);
});
}
I'd always prefer to keep PUT method idempotent. Idempotency can be explained as how many times you apply a certain "operation", the result will be the same as the first time. Since REST is just a style, it's up to you, but I will always question to me if it makes sense to keep the operation as PUT or POST.
What if the client of your service is impatient and access your PUT service multiple times while the first request is being served?. You may end up creating two users. So throwing an exception is meaningful if the ID doesn't exist.
It can be 400 or 404, I don't prefer 404 but prefer 400 because of the following reasons,
1) It confuses the client of your APIs if the resource is wrong or the ID they are using is wrong.
(You can always differentiate in your error response and provide meaningful information, but still, I don't prefer!)
2) By using 404,
you're telling the user the problem could be permanent or temporary
,for instance, say your service is not properly registered with discovery server(eureka) or is crashed, the discovery server will send 404 until you fix the problem.
By using 400,
you're asking the user to try with different input, in this case, with a different ID. This is permanent...
as you said id is auto-increment and the client cannot decide the value, so until the user fixes the problem by going back and request your POST service for a new ID, the request is "BAD" and cannot be processed.
Based on Single Responsibility Principle, you should have methods which are doing only one thing. So for your question, you need 2 methods for each request:
GET - asking the server for an object, in your case TestBean.
POST - save new objects (you don't need an id for these).
And in your front end application you could use the GET to ask the server if it have the requested object, and if not, maybe you can add a form which on submit will make the POST request with the data provided in the form fields.
PUT should only be responsible for updating a record. If the id of your bean doesn't exist, you will have an exception on your persistence layer. You can catch that exception on your API and return one of the 400's response code, such as BAD REQUEST.
For creation you should use POST, an id should not be provided in that case
This would be the RESTful way of doing this.
404 is the correct return code for a PUT to a non-existent resource, because the URL used does not address an extant resource.
If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.
If the server desires that the request be applied to a different URI, it MUST send a 301 (Moved Permanently) response; the user agent MAY then make its own decision regarding whether or not to redirect the request.
https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

Spring REST #Validated getting a "JBWEB000065: The request sent by the client was syntactically incorrect."

I have something like the following in a Spring project:
#RequestMapping(value = "/someRestUrl", method = RequestMethod.POST)
public SomeSo doSomeWork(#Validated #RequestBody SomeSo someSo) {
...
}
I recently changed SomeSo to have an additional parameter that was required:
#NotNull
private String someParameterThatNeedsToBeProvided;
Then I promptly left for the evening and when I got back to work the next day, my requests that were working before I made this change were no longer working. It took me forever to figure out why because I can't remember what I did less than 24 hours ago and, more importantly, because the error message sent to the client was only the following with no further details:
How can I get more information on what the issue is by either logging or sending this information back to the client? Is there some configuration I can do to get either more detailed logging for errors like this (I'd like to NOT include all of Spring's logging at DEBUG level, though) or provide this information to the client?
Note: I'd like to clarify that the request was parseable but was just missing the new parameter. It wasn't poorly formatted JSON.
You would want to check out the Errors class or the BindingResult class. Those give you details on what problems occurred due to #Validated. You can include them in the method parameters to interact with them. They are also available to templates. You would want to make a custom error page to output the details of the errors if that is something you want to expose.
I have faced this same error, and it was due to the incoming JSON not matching the object it is being mapped to.
Most probable cause is an empty collection, a single item mapped to a collection or some incorrect data type conversion.
You should enable DEBUG for Spring to detect the failure. Usually this errors are caused by inner exceptions from Jackson Mapper... So take a look at the log to find it, and you'll get an idea of what is the cause for your particular error.

How to handle different roles for multiple elements for a single user?

First of all, I'm new to spring-boot and spring-security.
I've read a lot of documentations (especially on http://www.spring.io) but I don't find a lot of information. I'm also working with JsonWebTokens, so all my authentication process is based on this. I followed this excellent tutorial to implement JsonWebToken authentication with spring-security : http://blog.jdriven.com/2014/10/stateless-spring-security-part-2-stateless-authentication/
So here is my problem :
I am working on a bug tracking application. For each project, multiples users may have different role (reporter, developer, admin, etc.). In the aforementioned tutorial, the authority is grant for the whole restful API, and the user only have one role for the entire API. However, in my case, I need to check first if the user has the good role for the good project (project is sent through http query parameter) and I have no clue how to handle this. To summarize, a user could call an URI like /tickets/close/{id} on a project, but not on another.
At the moment, my WebSecurityConfigurerAdapter overrides the configure() method like :
http
...
.antMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
.anyRequest().hasAuthority("ROLE_USER").and()
...
But it obviously don't take care of the query parameter in that case.
What you're after is not "different roles" for a user, rather authorizing a user for access to an object or set of objects.
Read through Domain ACLs, your users would be authorized differently for different projects, and the project would be the parent authorizing object for the issues, and issues would inherit from projects.
Since your security rules needs to consider your application domain, I would implement the security rules within your controllers. If your controllers detect that a user is trying to access a forbidden resources, you would respond by an ResponseEntity with a 503 error.
What I usually do is to throw a custom exception from my controller and define an Exception Handler. That exception handler would handle the 503 response. Here is an exemple :
#ControllerAdvice
public class ExceptionHandlerConfig {
#ExceptionHandler(ValidationException.class)
public ResponseEntity<Error> handleInvalidFileFormatException(HttpServletRequest request, RuntimeException ex) {
Error error = new Error(0, new Date(), request.getRemoteAddr(), request.getRemoteHost(),
request.getHeader("User-Agent"), ex.getMessage(), ExceptionUtils.getFullStackTrace(ex));
if (ex instanceof ValidationException) {
return new ResponseEntity<Error>(error, HttpStatus.BAD_REQUEST);
}
LOGGER.error("Error while processing request.", ex);
return new ResponseEntity<Error>(errorService.save(error), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
This is a custom ValidationException handler that generates a 500 HTTP response with the error message wrapped in a JSON object. You would reuse that for yourself with a custom SecurityException and flag the ResponseEntity with a 503 rather than a 500.
Best of luck.
Edit : ACL would definately be the best choice to keep the proper segregate of your business logic from the security aspect. However it might be complicated to setup if you are not familiar with Spring Security Advanced concepts.

How do I implement site-wide exception handling in a spring MVC architecture?

Note: Skip to the two updates at the bottom to find the answer.
I have a Spring (3) MVC web application running on Tomcat. This web application serves as a UI for a remote WSDL service layer. I have written a custom AuthenticationProvider to authenticate to this remote service. That works fine. My issue is this:
Each controller method, more or less, results in a remote call to this remote WSDL. My plan was to not have Tomcat or Spring expire sessions, but wait until the remote server tells me that the session has expired on that end and then re-route the user to the login page, telling them that their session has expired.
BaseController, from which all other Controllers extend, has this method:
protected void addBindingErrors(HttpSession session, MTAResponse response, BindingResult binding, boolean allowDefault) {
switch (response.getResponseCode()) {
case SESSION_EXPIRED:
UserInfo nfo = (UserInfo)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
SecurityContext context = SecurityContextHolder.getContext();
System.out.println("token in handle session expired: " + nfo.getMtaCoreSessionId());
if(session != null){
System.out.println("invalidating session");
session.invalidate();
//1. throw new SessionAuthenticationException("session expiredd");
ModelAndView mav = new ModelAndView("error");
throw new ModelAndViewDefiningException(mav);
//3. something else?
}
context.setAuthentication(null);
break;
case INVALID_PARAMETER_VALUE:
binding.addError(...);
break;
case ACCESS_DENIED:
binding.addError(...);
break;
default:
if(allowDefault){
binding.addError(...);
break;
}
}
}
I have found a couple solutions, but I'm new to Spring so I don't know if they're the "right" ones or if there are issues lurking in them.
(a bit of a cheat) In the BaseController.addBindingError() method, if a remote session expired code is received, throw a SessionAuthenticationException
Pros: this works, automagically re-routes the user to login page (I think because I have authentication-failure-url set?)
Cons: I’d have to change the signature of addBindingError to throw this kind of exception.
Con: I’d also have to change the signature of every controller method to throw this kind of exception, since they all make remote service calls to WSDL.
Con: the user doesn’t know why they’re back at the login page and I’m not sure there is a way to let them know
In the BaseController.addBindingError() method, if a remote session expired code is received, throw a ModelAndViewDefiningException
Pros: this works, automagically re-routes the user to appropriate error page, which can contain a link to the login page
Pro: we can explain to the user on this page how they got here.
Cons: I’d have to change the signature of addBindingError to throw this kind of exception.
Con: I’d also have to change the signature of every controller method to throw this kind of exception, since they all make remote service calls to remote WSDL
In the BaseController.addBindingError() method, if a remote session expired code is received, throw a MyCustomRemoteSessionExpiredException and then catch it in a SimpleMappingExceptionResolver
Pros: this works, automagically re-routes the user to appropriate error page, which can contain a link to the login page
Pro: we can explain to the user on this page how they got here.
Cons: I’d have to change the signature of addBindingError to throw this kind of exception.
Con: I’d also have to change the signature of every controller method to throw this kind of exception, since they all make remote service calls to remote WSDL
Numbers 2 and #3 have the same pros/cons. #3 seems like it might be a more "correct" solution. Does anyone have any feedback, suggestions, or alternatives?
Update 5/30/2013:
I think I may have found what I'm looking for and I think it will require me to upgrade to the latest spring 3.2. #ControllerAdvice a la this article here:
http://www.javacodegeeks.com/2013/03/exception-handling-with-the-spring-3-2-controlleradvice-annotation.html
From BaseController.addBindingError, I'll throw MyCustomException (which extends RuntimeException so that the method signatures will not need to declare a throws statement) and handle that in a new #ControllerAdvice class (which will contain an #ExceptionHandler method).
Can anyone confirm or deny this approach?
Any better suggestions?
Thanks!
Update 12/3/2013 **
This question would have been much better titled like so: "How do I implement site-wide exception handling in a spring MVC architecture?". So, I'm changing the title (the former title was based on my misunderstanding that the interaction with the remote SOAP server had anything to do with my real question). I implemented the solution discussed in May and it has been working very well. I have even extended the #ControllerAdvice class to handle other RuntimeExceptions.

Passing errors back to the view from the service layer

Edit: I have looked into Spring 3's #ExceptionHandler annotation and combining this with Option 1 below looks to be a pretty clean solution.
See http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers
I also found this to be a good read: http://blog.decaresystems.ie/index.php/2006/04/07/difficult-choices-in-handling-exceptions-in-enterprise-java-applications/
I have been developing using the Spring MVC framework for some time now however I am struggling to come up with a 'nice' way to pass errors that are raised in the service layer back to the JSP.
Basically, I don't believe that business logic (beyond "this field is mandatory") should be in the Validators, especially any logic that requires access to the DB. So, what I have been doing is placing further, more complicated validation and business logic in the service layer.
For example, lets say I have a page that allows a user to buy a Book. They click "Purchase" on the JSP and the controller calls the service to make it all happen... Now, what happens if the service sees that they have insufficient funds - how do I get this message back to the JSP so a nice little "Insufficient funds" message can be displayed to the user? I have considered two ways and I'm not sure which is correct...
Option 1: Exceptions
The first way I thought was to raise an exception in the service layer, trap it in the controller and add a message to the BindingResult.
Service:
public void pay(Book book) throws InsufficientFundsException {
// Some logic goes here, which ends up throwing the above exception
}
Controller:
public ModelAndView(#ModelAttribute("book") Book book, BindingResult errors) {
try {
pay(book);
} catch (InsufficientFundsException ex) {
errors.reject("insufficient.funds");
}
return new ModelAndView(blahblahblah);
}
Option 2: Pass BindingResult to Service layer
The second way was to pass the BindingResult object to the service layer and raise further errors against it.
Service:
public void pay(Book book, BindingResult errors) {
// User has insufficient funds, so...
errors.reject("insufficient.funds);
}
I can see problems with both of these ways. Option 1 feels awkward because not only do I have to catch the exception, I then have to add the error to the binding result so it feels like I'm doing the same thing twice. And Option 2 seems to bind the service layer too tightly to the controller.
Finally, I realise there is the SimpleMappingExceptionResolver that could be used in conjunction with Option 1, but I'm not sure how appropriate it is (perhaps I have not seen a proper example?). In the above example, lets just say for argument's sake that I'd like the user returned to the original form with a red error above the form, not redirected to an entirely different page. The SimpleMappingExceptionResolver seems to me to be useful when you want to redirect a user to a standard error page when a certain exception is raised (which is not quite what I want to know how to do).
Java uses exceptions to naturally handle this kind of thing. In the end it generally simplifies your logic and reduces the chance of making a mistake by forgetting to check that something had an error. You are also able to move error logic out of the main flow of the code.
I don't see why the case you present is different from any other case where I would use exception handling to deal with errors.

Categories

Resources