how to return not found status from spring controller - spring

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")

Related

UpdateById method in Spring Reactive Mongo Router Handler

In Spring Reactive Java how can I write an updateById() method using the Router and Handler?
For example, the Router has this code:
RouterFunctions.route(RequestPredicates.PUT("/employees/{id}").and(RequestPredicates.accept(MediaType.APPLICATION_JSON))
.and(RequestPredicates.contentType(MediaType.APPLICATION_JSON)),
employeeHandler::updateEmployeeById);
My question is how to write the employeeHandler::updateEmployeeById() keeping the ID as same but changing the other members of the Employee object?
public Mono<ServerResponse> updateEmployeeById(ServerRequest serverRequest) {
Mono<Employee> employeeMono = serverRequest.bodyToMono(Employee.class);
<And now what??>
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(employeeMono, Employee.class);
}
The Employee class looks like this:
#Document
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Employee {
#Id
int id;
double salary;
}
Thanks for any help.
First of all, you have to add ReactiveMongoRepository in your classpath. You can also read about it here.
#Repository
public interface EmployeeRepository extends ReactiveMongoRepository<Employee, Integer> {
Mono<Employee> findById(Integer id);
}
Then your updateEmployeeById method can have the following structure:
public Mono<ServerResponse> updateEmployeeById(ServerRequest serverRequest) {
return serverRequest
.bodyToMono(Employee.class)
.doOnSubscribe(e -> log.info("update employee request received"))
.flatMap(employee -> {
Integer id = Integer.parseInt(serverRequest.pathVariable("id"));
return employeeRepository
.findById(id)
.switchIfEmpty(Mono.error(new NotFoundException("employee with " + id + " has not been found")))
// what you need to do is to update already found entity with
// new values. Usually map() function is used for that purpose
// because map is about 'transformation' what is setting new
// values in our case
.map(foundEmployee -> {
foundEmployee.setSalary(employee.getSalary());
return foundEmployee;
});
})
.flatMap(employeeRepository::save)
.doOnError(error -> log.error("error while updating employee", error))
.doOnSuccess(e -> log.info("employee [{}] has been updated", e.getId()))
.flatMap(employee -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(employee), Employee.class));
}
UPDATE:
Based on Prana's answer, I have updated the code above merging our solutions in one. Logging with a help of Slf4j was added. And switchIfEmpty() functions for the case when the entity was not found.
I would also suggest your reading about global exception handling which will make your API even better. A part of it I can provide here:
/**
* Returns routing function.
*
* #param errorAttributes errorAttributes
* #return routing function
*/
#Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
private HttpStatus getStatus(Throwable error) {
HttpStatus status;
if (error instanceof NotFoundException) {
status = NOT_FOUND;
} else if (error instanceof ValidationException) {
status = BAD_REQUEST;
} else {
status = INTERNAL_SERVER_ERROR;
}
return status;
}
/**
* Custom global error handler.
*
* #param request request
* #return response
*/
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
Map<String, Object> errorPropertiesMap = getErrorAttributes(request, false);
Throwable error = getError(request);
HttpStatus errorStatus = getStatus(error);
return ServerResponse
.status(errorStatus)
.contentType(APPLICATION_JSON)
.body(BodyInserters.fromValue(errorPropertiesMap));
}
A slightly different version of the above worked without any exceptions:
public Mono<ServerResponse> updateEmployeeById(ServerRequest serverRequest) {
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
Mono<Employee> employeeMono = serverRequest.bodyToMono(Employee.class);
Integer employeeId = Integer.parseInt(serverRequest.pathVariable("id"));
employeeMono = employeeMono.flatMap(employee -> employeeRepository.findById(employeeId)
.map(foundEmployee -> {
foundEmployee.setSalary(employee.getSalary());
return foundEmployee;
})
.flatMap(employeeRepository::save));
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(employeeMono, Employee.class).switchIfEmpty(notFound);
}
Thanks to Stepan Tsybulski.

How spring mvc handle responsebody and view excpetion?

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.

How to bind a cookie value to a pojo field in Spring MVC?

For example, I'd like to bind the value of the cookie in the current request to the pojo below.
#RequestMapping(path="/", method= GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> handleTheRequest(Foo foo){
return blah;
}
public class Foo{
private string cookieValue;
//Other fun fields
public void setCookieValue(String value){
this.cookieValue = value;
}
public string getCookieValue(){
return cookieValue;
}
}
Can you try ?
#RequestMapping(path="/", method= GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> handleTheRequest(#RequestBody Foo foo){
return blah;
}

#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)
);
}

Spring : timely/late binding of #ModelAttribute

I'm using the following code to bind the users to the model [to be used in the view/jsp]:
#ModelAttribute("users")
public Collection<User> populateUsers() {
return userService.findAllUsers();
}
But sometimes I just need to load few users with a particular Role, which I'm trying by using the following code:
int role = 2; //this is being set in a Controller within a method #RequestMapping(method = RequestMethod.GET) public String list(
#ModelAttribute("users")
public Collection<User> populateUsers() {
if(role == 2)
return userService.findAllUsersByRole(role);
else
return userService.findAllUsers();
}
but the populateUsers is always called at the start of the controller, before the role is set in list method, Could you please help me on how to set the users [something like late binding]
Regards
-- adding code
#Controller
#RequestMapping("/users")
public class UserController {
#Autowired
UserService userService;
#RequestMapping(method = RequestMethod.POST)
public String create(#Valid User user, BindingResult bindingResult,
Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
uiModel.addAttribute("user", user);
addDateTimeFormatPatterns(uiModel);
return "users/create";
}
uiModel.asMap().clear();
userService.saveUser(user);
return "redirect:/users/"
+ encodeUrlPathSegment(user.getId().toString(),
httpServletRequest);
}
#RequestMapping(params = "form", method = RequestMethod.GET)
public String createForm(Model uiModel) {
uiModel.addAttribute("user", new User());
addDateTimeFormatPatterns(uiModel);
return "users/create";
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String show(#PathVariable("id") Long id, Model uiModel) {
addDateTimeFormatPatterns(uiModel);
uiModel.addAttribute("user", userService.findUser(id));
return "users/show";
}
#RequestMapping(value = "/{id}", params = "form", method = RequestMethod.GET)
public String updateForm(#PathVariable("id") Long id, Model uiModel) {
uiModel.addAttribute("user", userService.findUser(id));
addDateTimeFormatPatterns(uiModel);
return "users/update";
}
#ModelAttribute("users")
public Collection<User> populateUsers() {
return userService.findAllUsers();
}
#ModelAttribute("userroles")
public Collection<UserRole> populateUserRoles() {
return Arrays.asList(UserRole.class.getEnumConstants());
}
void addDateTimeFormatPatterns(Model uiModel) {
uiModel.addAttribute(
"user_modified_date_format",
DateTimeFormat.patternForStyle("M-",
LocaleContextHolder.getLocale()));
}
}
#PathVariable("id") Long id is the ID I require in populateUsers, hope it is clear.
If role is in the current request, this method binding role to variable role.
#ModelAttribute("users")
public Collection<User> populateUsers(#RequestParam(required=false) Integer role) {
if(role != null && role == 2)
return userService.findAllUsersByRole(role);
else
return userService.findAllUsers();
}
Setting the model attribute in the required method has solved my issue:
model.addAttribute("users", return userService.findAllUsersByRole(role));
Thanks!

Resources