How spring mvc handle responsebody and view excpetion? - spring

I have a controller as
#Controller
#RequestMapping("/test")
public class TestController {
#RequestMapping("/1")
#ResponseBody
public String test1(){
Object o = null;
o.toString();
return "I ma test one!";
}
#RequestMapping("/2")
public String test2(){
Object o = null;
o.toString();
return "test";
}
}
Is it possible to create ControllerAdvice(s) to handle the controller method as different result without moving these to message to different classes.
I mean:
1. test1 returns a String message: if there is exception, handle it with handleError1 and return a message.
2. test1 returns a view : if there is exception, handle it with handleError2 and return/redirect to a view.
#ControllerAdvice
public class AdviceController {
#ExceptionHandler({ NullPointerException.class })
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public Map handleError1(IllegalStateException ex, HttpServletRequest request) {
Map map = new HashMap();
map.put("code","1000");
map.put("message","NullPointerException of Object");
return map;
}
#ExceptionHandler(NullPointerException.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public String handleError2(MultipartException e, RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("message", e.getCause().getMessage());
redirectAttributes.addFlashAttribute("code", "1000");
return "redirect:/error";
}
}
if use
#ControllerAdvice(annotations=RestController.class)
#ControllerAdvice(annotations=Controller.class)
We need to create more controllers.

Related

FeignException com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.http.ResponseEntity`

Any Help please !!
I receive this error when I'm calling my endpoint which call Feign in the background :
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
`org.springframework.http.ResponseEntity` (no Creators, like default constructor, exist): cannot deserialize
from Object value (no delegate- or property-based Creator)
at [Source: (BufferedReader); line: 1, column: 2]
This is my endpoint inside Controller :
#RestController
#RequestMapping(Routes.URI_PREFIX)
public class CartoController {
#Autowired
private ReadCartographyApiDelegate readCartographyApiDelegate;
#GetMapping(value = "/cartographies/{uid}", produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseWrapper<ReadCartographyResponse> readCarto(HttpServletRequest request,
#PathVariable(name = "uid") String uid) {
ResponseEntity<ReadCartographyResponse> result ;
try {
result = readCartographyApiDelegate.readCartography(uid);
}catch (Exception e){
throw new TechnicalException("Error during read Carto");
}
return responseWrapperWithIdBuilder.of(result.getBody());
}
}
Interface ReadCartographyApiDelegate generated automatically by openApi from yaml file :
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "...")
public interface ReadCartographyApiDelegate {
default Optional<NativeWebRequest> getRequest() {
return Optional.empty();
}
default ResponseEntity<ReadCartographyResponse> readCartography(String uid) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "null";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
This my ReadCartoApiDelegateImpl which implements ReadCartographyApiDelegate interface :
#Service
public class ReadCartographyApiDelegateImpl implements ReadCartographyApiDelegate {
private EcomGtmClient ecomGtmClient;
public ReadCartographyApiDelegateImpl(EcomGtmClient ecomGtmClient) {
this.ecomGtmClient = ecomGtmClient;
}
#Override
public ResponseEntity<ReadCartographyResponse> readCartography(String uid) {
ResponseEntity<ReadCartographyResponse> response = ecomGtmClient.readCartography(uid);
return response;
}
}
This is the feign client :
#FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
#GetMapping(value = "/read-carto/{uid}")
ResponseEntity<ReadCartographyResponse> readCartography(#PathVariable("uid") String uid);
}
The problem is that ResponseEntity (spring class) class doesn't contain default constructor which is needed during creating of instance. is there Any config to resolve this issue ?
If you want access to the body or headers on feign responses, you should use the feign.Response class. ResponseEntity does not work with feign because it is not meant to. I think it is best if you just return Response from your feign client method. You should then be able to pass the body to the ResponseEntity instance in the Controller.
What is your reason to even use the response-wrapper, i can't really figure that out from your code?
Sadly I couldn't find any documentation on the Response class, but here's the link to the source on GitHub.
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Response.java
My Suggestion would be
#FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
#GetMapping(value = "/read-carto/{uid}")
ReadCartographyResponse readCartography(#PathVariable("uid") String uid);
}
#RestController
#RequestMapping(Routes.URI_PREFIX)
public class CartoController {
#Autowired
private ReadCartographyApiDelegate readCartographyApiDelegate;
#GetMapping(value = "/cartographies/{uid}", produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseWrapper<ReadCartographyResponse> readCarto(HttpServletRequest request,
#PathVariable(name = "uid") String uid) {
ReadCartographyResponse result ;
try {
result = readCartographyApiDelegate.readCartography(uid);
}catch (Exception e){
throw new TechnicalException("Error during read Carto");
}
// I don't know where you get the builder from, so I assume it does something import and is needed
return responseWrapperWithIdBuilder.of(result);
}
}
Of course you'd also have to change all intermediate classes.
The Response Output was the correct Object that I have to put, cause every time I need to check the status from my feign client endpoint to do différent logic
#FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
#GetMapping(value = "/read-carto/{uid}")
ReadCartographyResponse readCartography(#PathVariable("uid") String uid);
}

#PathVariable Validation in Spring 4

How can i validate my path variable in spring. I want to validate id field, since its only single field i do not want to move to a Pojo
#RestController
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(#PathVariable String id) {
/// Some code
}
}
I tried doing adding validation to the path variable but its still not working
#RestController
#Validated
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(
#Valid
#Nonnull
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable String id) {
/// Some code
}
}
You need to create a bean in your Spring configuration:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
You should leave the #Validated annotation on your controller.
And you need an Exceptionhandler in your MyController class to handle theConstraintViolationException :
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
After those changes you should see your message when the validation hits.
P.S.: I just tried it with your #Size validation.
To archive this goal I have apply this workaround for getting a response message equals to a real Validator:
#GetMapping("/check/email/{email:" + Constants.LOGIN_REGEX + "}")
#Timed
public ResponseEntity isValidEmail(#Email #PathVariable(value = "email") String email) {
return userService.getUserByEmail(email).map(user -> {
Problem problem = Problem.builder()
.withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
.withTitle("Method argument not valid")
.withStatus(Status.BAD_REQUEST)
.with("message", ErrorConstants.ERR_VALIDATION)
.with("fieldErrors", Arrays.asList(new FieldErrorVM("", "isValidEmail.email", "not unique")))
.build();
return new ResponseEntity(problem, HttpStatus.BAD_REQUEST);
}).orElse(
new ResponseEntity(new UtilsValidatorResponse(EMAIL_VALIDA), HttpStatus.OK)
);
}

how to return not found status from spring controller

I have following spring controller code and want to return not found status if user is not found in database, how to do it?
#Controller
public class UserController {
#RequestMapping(value = "/user?${id}", method = RequestMethod.GET)
public #ResponseBody User getUser(#PathVariable Long id) {
....
}
}
JDK8 approach:
#RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public ResponseEntity<User> getUser(#PathVariable Long id) {
return Optional
.ofNullable( userRepository.findOne(id) )
.map( user -> ResponseEntity.ok().body(user) ) //200 OK
.orElseGet( () -> ResponseEntity.notFound().build() ); //404 Not found
}
Change your handler method to have a return type of ResponseEntity. You can then return appropriately
#RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public ResponseEntity<User> getUser(#PathVariable Long id) {
User user = ...;
if (user != null) {
return new ResponseEntity<User>(user, HttpStatus.OK);
}
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Spring will use the same HttpMessageConverter objects to convert the User object as it does with #ResponseBody, except now you have more control over the status code and headers you want to return in the response.
With the latest update you can just use
return ResponseEntity.of(Optional<user>);
The rest is handled by below code
/**
* A shortcut for creating a {#code ResponseEntity} with the given body
* and the {#linkplain HttpStatus#OK OK} status, or an empty body and a
* {#linkplain HttpStatus#NOT_FOUND NOT FOUND} status in case of a
* {#linkplain Optional#empty()} parameter.
* #return the created {#code ResponseEntity}
* #since 5.1
*/
public static <T> ResponseEntity<T> of(Optional<T> body) {
Assert.notNull(body, "Body must not be null");
return body.map(ResponseEntity::ok).orElse(notFound().build());
}
public static ResponseEntity of(Optional body)
A shortcut for creating a ResponseEntity with the given body and the OK status, or an empty body and a NOT FOUND status in case of an Optional.empty() parameter.
#GetMapping(value = "/user/{id}")
public ResponseEntity<User> getUser(#PathVariable final Long id) {
return ResponseEntity.of(userRepository.findOne(id)));
}
public Optional<User> findOne(final Long id) {
MapSqlParameterSource paramSource = new MapSqlParameterSource().addValue("id", id);
try {
return Optional.of(namedParameterJdbcTemplate.queryForObject(SELECT_USER_BY_ID, paramSource, new UserMapper()));
} catch (DataAccessException dae) {
return Optional.empty();
}
}
it could be shorter using Method Reference operator ::
#RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public ResponseEntity<User> getUser(#PathVariable Long id) {
return Optional.ofNullable(userRepository.findOne(id))
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
Need use ResponseEntity or #ResponseStatus, or with "extends RuntimeException"
#DeleteMapping(value = "")
public ResponseEntity<Employee> deleteEmployeeById(#RequestBody Employee employee) {
Employee tmp = employeeService.deleteEmployeeById(employee);
return new ResponseEntity<>(tmp, Objects.nonNull(tmp) ? HttpStatus.OK : HttpStatus.NOT_FOUND);
}
or
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="was Not Found")

When to use HttpServletRequest class in a Spring Mvc Controller

I am reading a book about spring and in the chapter about spring mvc the author list the following controller code that is responsible for form submission.
My question (since the author is not referring to it is why and where we should use HttpServletRequest)
Here is the method :
#RequestMapping(value = "/{id}", params = "form", method = RequestMethod.POST)
public String update(#Valid Contact contact, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes, Locale locale)
{
logger.info("Updating contact");
if (bindingResult.hasErrors())
{
uiModel.addAttribute("message", new Message("error", messageSource.getMessage("contact_save_fail", new Object[]{}, locale)));
uiModel.addAttribute("contact", contact);
return "contacts/update";
}
uiModel.asMap().clear();
redirectAttributes.addFlashAttribute("message", new Message("success", messageSource.getMessage("contact_save_success", new Object[]{}, locale)));
contactService.save(contact);
return "redirect:/contacts/" + UrlUtil.encodeUrlPathSegment(contact.getId().toString(), httpServletRequest);
}
Use it whenever you need to use it...
In this example, the author is using it get the character encoding :
return "redirect:/contacts/" + UrlUtil.encodeUrlPathSegment(contact.getId().toString(), httpServletRequest);
Here is the code from the UrlUtil class :
public class UrlUtil {
public static String encodeUrlPathSegment(String pathSegment, HttpServletRequest
httpServletRequest) {
String enc = httpServletRequest.getCharacterEncoding();
if (enc == null) {
enc = WebUtils.DEFAULT_CHARACTER_ENCODING;
}
try {
pathSegment = UriUtils.encodePathSegment(pathSegment, enc);
} catch (UnsupportedEncodingException uee) {
}
return pathSegment;
}
}
More information about the HttpServletRequest class :
It extends the ServletRequest interface to provide request information for HTTP servlets. You might consider reading the javadoc if you want to learn more about the methods of the class.

Spring MVC ExceptionHandling: action annotated as #ExceptionHandling can't pass variable to error view

I know a lot of people have had issues similar to this.Sorry posting it again, but i believe there is something i might not be doing well.
I'm using Spring 3.0.5 with freemarker 2.3.14. Basically i wanted to show a friendly error message to the user.
#Controller("exceptioncontroller")
public class ExceptionController {
private static Logger logger = Logger.getLogger(ExceptionController.class);
#RequestMapping(value = "/site/contentnofoundexception")
public String throwContentFileNotFound(){
boolean exception = true;
if(exception){
throw new ContentFileNotFoundException("content ZZZ123 not found");
}
return "errortest";
}
#ExceptionHandler(value = ContentFileNotFoundException.class)
public String handleFileNotFoundException(ContentFileNotFoundException ex, Model model) {
model.addAttribute("msg",ex.getErrorMessage());//this message is never passed to the error view. msg is always null
return "error";
}
}
//same issue for handleException action which uses ModelAndView
#ExceptionHandler(value = Exception.class)
public ModelAndView handleException(Exception ex){
logger.error(ex);
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
String message = "Something Broke. Please try again later";
mv.addObject("msg", message);
return mv;
}
// Custom Exception class
public class ContentFileNotFoundException extends RuntimeException {
private String errorMessage;
public ContentFileNotFoundException(String message) {
this.setErrorMessage(message);
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
So each case either handleFileNotFoundException or handleException actions are called alright but they can't send any message to the error.ftl view to display to the user. Is there anything i need to configure?
Thanks for helping in advance

Resources