How to handle Optional from Service to Controller in Spring? - spring

Spring JPA returns an Optional. I return the Optional from the Service. There if Optional is not present I pass error to model. The other case if there is a database error for example database not available, I do not catch these exceptions. If that happens user will see this exception in browser. I do not know how to handle this very rare error. For me this should never happen and if it does, ok . I do not want to handle this exception all the time. What do you think about my architecture.
Service:
#Override
public Optional<Client> findClientById(Long id) {
return clientRepository.findById(id);
}
Controller:
Optional<Client> client= clientService.findClientById(id);
if(client.isPresent())
{
model.addAttribute("client", client.get());
}
else
{
model.addAttribute("error", "No clientfound with this ID!!");
}

Firstly, you shouldn't pass error as an attribute in your model - that's REST anti-pattern. The best way to hand this is to use HTTP codes, e.g. return 502. To do so, you may wrap your exceptions up your code into HttpResponse. To catch your exception, you may approach similar to method that explained here, i.e. catch spring data exception, wrap it up a way you wanted and throw on higher level for processing.

Related

Respect\Validation\Validator - using an array while catching errors

I am attempting to catch errors utilizing the Respect\Validation\Validator opensource PHP class. I used their example to create an array of checks. Although that seems to work ok, I then attempted to catch any error messages so that I could display it to the user. I saw no method to do so as a full array (check everything, store all messages in an array). So instead, I tried to cycle through using the check method in Validator.
This is inside of a class method, using the F3 (Fat Free) Framework.
I end up with the following error:
Cannot use object of type Respect\Validation\Validator as array
The code is below. What is the proper way to perform this task using arrays here? Thank you for the assistance!
$registerValidator =
Respect\Validation\Validator::attribute('email', Respect\Validation\Validator::email()->length(1,null)->notEmpty())
->attribute('address', Respect\Validation\Validator::stringType()->length(3,null)->notEmpty())
->attribute('city', Respect\Validation\Validator::alpha()->length(2,60)->notEmpty())
->attribute('state', Respect\Validation\Validator::alpha()->length(2,2)->notEmpty())
->attribute('zip', Respect\Validation\Validator::intType()->length(5,5)->notEmpty());
foreach($this->f3->get('POST') as $key => $value){
try{
$registerValidator[$key]->check($value);
} catch (\InvalidArgumentException $e) {
$errors = $e->getMainMessage();
$this->userMessage($errors, 'warning');
$this->f3->reroute('/register');
}
}
I have also tried to use the assert method as found in their docs, but utilizing the below change, I get a different error at a 500 Server Internal Error, instead of seeing my echo:
try{
$registerValidator->assert($this->f3->get('POST'));
} catch (Respect\Validation\Validator\NestedValidationException $e) {
$errors = $e->getMessages();
echo($errors); // I can't even get here.
foreach($errors as $error){
$this->userMessage($error, 'warning');
}
$this->f3->reroute('/register');
}
With this 500 Error, rather than seeing my Echo, so the page stops loading entirely.
All of the required rules must pass for ...
You cannot really use the Validator class as an array like you're doing on $registerValidator[$key]->check($value). The object in $registerValidator variable contain the chain of rules to validate an input.
In your case I believe the input is the array coming from the POST, so first of all you should use the Key validator instead of Attribute.
However the real reason why you cannot catch the errors is because you have a typo on your catch statement, the class name should be Respect\Validation\Exceptions\NestedValidationException like it's stated in the documentation, not Respect\Validation\Validator\NestedValidationException.

How to send a response from a method that is not the controller method?

I've got a Controller.php whose show($id) method is hit by a route.
public function show($id)
{
// fetch a couple attributes from the request ...
$this->checkEverythingIsOk($attributes);
// ... return the requested resource.
return $response;
}
Now, in checkEverythingIsOk(), I perform some validation and authorization stuff. These checks are common to several routes within the same controller, so I'd like to extract these checks and call the method everytime I need to perform the same operations.
The problem is, I'm unable to send some responses from this method:
private function checkEverythingIsOk($attributes)
{
if (checkSomething()) {
return response()->json('Something went wrong'); // this does not work - it will return, but the response won't be sent.
}
// more checks...
return response()->callAResponseMacro('Something else went wrong'); // does not work either.
dd($attributes); // this works.
abort(422); // this works too.
}
Note: Yes, I know in general one can use middleware or validation services to perform the checks before the request hits the controller, but I don't want to. I need to do it this way.
As of Laravel 5.6 you can now use for example response()->json([1])->send();.
There is no need for it to be the return value of a controller method.
Note that calling send() will not terminate the output. You may want to call exit; manually after send().
You are probably looking for this:
function checkEverythingIsOk() {
if (checkSomething()) {
return Response::json('Something went wrong');
}
if(checkSomethingElse()) {
return Response::someMacro('Something else is wrong')
}
return null; // all is fine
}
And in the controller method:
$response = $this->checkEverythingIsOk();
if($response !== null) { // $response instanceof Response
return $response;
}
It's probably overkill, but I will throw it in anyway. You might want to look into internal requests. Also this is just pseudoish code, I have not actually done this, so take this bit of information with caution.
// build a new request
$returnEarly = Request::create('/returnearly');
// dispatch the new request
app()->handle($newRequest);
// have a route set up to catch those
Route::get('/returnearly', ...);
Now you can have a Controller sitting at the end of that route and interpret the parameters, or you use multiple routes answered by multiple Controllers/Methods ... up to you, but the approach stays the same.
UPDATE
Ok I just tried that myself, creating a new request and dispatching that, it works this way. Problem is, the execution does not stop after the child-request has exited. It goes on in the parent request. Which makes this whole approach kind of useless.
But I was thinking about another way, why not throw an Exception and catch it in an appropriate place to return a specified response?
Turns out, thats already built into Laravel:
// create intended Response
$response = Response::create(''); // or use the response() helper
// throw it, it is a Illuminate\Http\Exception\HttpResponseException
$response->throwResponse();
Now usually an Exception would be logged and you if you are in Debug mode, you would see it on screen etc. etc. But if you take a look into \Illuminate\Foundation\Exceptions\Handler within the render method you can see that it inspects the thrown Exception if it is an instance of HttpResponseException. If it is then the Response will be returned immediately.
To me the most simple and elegant way is:
response()->json($messages_array, $status_code)->throwResponse();
(you don`t need return)
It can be called from a private function or another class...
I use this in a helper class to check for permissions, and if the user doesn`t have it I throw with the above code.

Different response from a controller method based on HTTP Response code

How do you write a controller method which can either return a View or HTTP response status code based on if its 200 then view else the response status code.
#RequestMapping(value="/",method=RequestMethod.GET)
public String showLanding()
{
return View.Landing;
}
I want to handle in case of 401, 403, 500 etc. just status code should be returned instead of view.
To return the 403- Unauthorized status code,
#RequestMapping(value="/",method=RequestMethod.GET)
public String showLanding()
{
return HttpStatus.UNAUTHORIZED;
}
See this:
http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/http/HttpStatus.html?is-external=true
How to respond with HTTP 400 error in a Spring MVC #ResponseBody method returning String?
You could also check out the #ResponseStatus annotation as well as ResponseEntity (for more dynamic scenarios)
I'm far from suggesting that you should do flow control with Exceptions - but those HTTP statuses are errors and exceptions. So you might want to throw business exceptions from your controller methods and then handle those using #ExceptionHandlers.
You can also target a subset of Controllers and assist those with Exception handling using #ControllerAdvice.

Spring mvc controller null return handler

#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public List<Country> getListOfCountries() {
return countryService.listAll();
}
It displays a json view of the object but if the service return null, then I want to display an error message, Any suggestions pls?
First of all, even if this does not directly answer the question, your objects should never ever return null instead of empty collections - you can find the reasoning in Effective Java 2nd Edition, Item 43 / p.201
So, if the situation when no countries were found is normal it must be processed by the client JS code that will check the count and display the respective message.
If something has gone wrong you can throw an exception(as Biju has pointed out +1) - I believe that it's the service who should throw the exception because it knows the reason why it happened, and not to return null anyway.
I'd like to add that in Spring 3.2(in pre Spring 3.2 returning response body is complicated) you can set an #ExceptionHandler that will both return JSON and set the HTTP status code which can be later processed by the client. I think that returning a custom JSON response with some error code is most optimal here.
#RequestMapping("/test")
#ResponseBody
public List<Country> getListOfCountries() {
//assuming that your service throws new NoCountriesFoundException();
//when something goes wrong
return countryService.listAll();
}
#ExceptionHandler(NoCountriesFoundException.class)
ResponseEntity<String> test() {
return new ResponseEntity<String>(
"We are sorry, our server does not know any countries yet.",
HttpStatus.I_AM_A_TEAPOT );
}
Then in the JS code, you can do specific processing depending on the returned status code.
Also, to avoid declaration of the same #ExceptionHandler in different controllers, in Spring 3.2 you can put #ExceptionHandler inside a #ControllerAdvice annotated class.
For details, see http://static.springsource.org/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers and http://www.springsource.org/node/3738 for 3.2 specific things
You have a couple of options I think:
If you return a null back, it will be returned as an empty string "", you can probably look for that and handle it.
Return a wrapper type on top of your list, this way if the wrapped list is null something like this will be returned back to the client {"countries":null} which can be more easily handled at the javascript end.
Throw an exception, which will propagate as a 500 status code back to the client, you can then have an error handler on the javascript side to handle this scenario.

MVC3 What Level Should I Catch Errors

I have a website which is utilzing MVC3. I have an n-tier architecture and I am curious at what level it is best to catch errors.
For isntance let's say I have a Students table I have a StudentRepository with a function such as:
StudentRepository.GetHightestGrade(studentId)
So should I have my Repositry function have a try/catch block - or should I put the try/catch directly into the ActionResult function. OR would I be better served adding in a business class and then my ActionResult function would do something such as
Business.GetHighestGrade(studentId) and that function simply has a try/catch and calls the Repository function?
You should really only wrap methods in try/catch if you are trying to prevent errors from bubbling up the stack. Generally it's best to put the try/catch at the top layers, to shield the users from the errors. Lower layers should generally throw exceptions during exceptional circumstances, you should only try to catch them in higher layers.
You will find your code is much more readable without a ton of try/catch blocks, I personally try to avoid them when I can, and let the MVC3 HandleError filter attribute take care of displaying error messages. However sometimes you may want to retry an operation if it throws an exception, which makes a good try/catch candidate.
Take a look at ELMAH -- using it should help you write code that avoids exceptions in the first place. But you should only explicitly try/catch if you are expecting an exception, and want to take some action in response to it.
it depends on what you wanna do in the catch, do you just wanna fail silently? do you wanna log the error? do you wanna return a view to notify the user that something went wrong?
if you just wanna notify the user, then catch it in the controller.
if you wanna log it, then catch it in your repo, and have your repo do the logging through some logging service, you could use some higher service that does the call catch the error and log it, but that might be an over kill.
you should write a global filter attribute for custom exceptions. Catch all exception over that filter and return to view. Something like that
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Text;
namespace Filters
{
public sealed class HandleException : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
throw new ArgumentException("filterContext");
else if (typeof(AjaxException).IsInstanceOfType(filterContext.Exception) && !filterContext.ExceptionHandled)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.ContentEncoding = Encoding.UTF8;
filterContext.HttpContext.Response.HeaderEncoding = Encoding.UTF8;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = 400;
filterContext.Result = new ContentResult
{
Content = "Unexpected error",
ContentEncoding = Encoding.UTF8,
};
}
}
}
}
and add this filter to global filter atributes in global.asax
Repository level must have try-catch construction that handle sql exceptions and write errors to Log. If your business logic is complicated and can produce errors or inconsistent states - you should check it and write errors to Log too. If you want to notify user about something gonna wrong (if business logic provide exceptions) - you should use try-catch constructions in your controller actions and use ModelState.AddModelError(), as one of ways to notify user, or use other way to pass info about error into view. And finally you must have class CustomErrorFilter : IExceptionFilter registered in global.asax in Application_Start:
GlobalFilters.Filters.Add(new LoggingFilter());
That filter should write to Log all unhandled errors that occurs on controller level.
Remember about your code provide normal workflow and can provide error workflow (throw exceptions as one of ways to organize error workflow, or return some error codes). You should count on it and handle both workflows.

Resources