I am implementing Spring Data REST in one of my project. I have to write a few custom Repository in order to write custom queries. I am using a Controller in front of my REST Repository. In order to get a HATEOAS response, I am using PersistentEntityResourceAssembler in my controller. This controller works fine for a single entity but in case of a list, I get an exception "PersistentEntity must not be null!"
#RequestMapping(value="/employmentType", method=RequestMethod.GET, produces="application/hal+json")
#ResponseBody
public ResponseEntity<?> getEmploymentTypes(HttpServletRequest request, HttpServletResponse response,PersistentEntityResourceAssembler resourceAssembler) throws TenantUnavailableException, TenantInvalidException
{
try
{
List<EmploymentType> employmentTypeList = employmentTypeRepository.findAll();
if(null==employmentTypeList || employmentTypeList.size()==0)
return new ResponseEntity<ApiResponse>(new ApiResponse(false, ENTITY_NOT_FOUND),
HttpStatus.NOT_FOUND);
// Accessing the 0th index works fine
//In case of a full list, it throws "Persistant Entity must not be null !" exception
return ResponseEntity.ok(resourceAssembler.toResource(employmentTypeList.get(0)));
}
catch (Exception e)
{
e.printStackTrace();
return new ResponseEntity<ApiResponse>(new AppResponse(false, REQUEST_NOT_PROCESSED),
HttpStatus.INTERNAL_SERVER_ERROR);
}
}
I am trying to leverage maximum spring functionality with minimum coding support from my end. I do not want to write a ResourceAssembler for each and every persistent entity in my project.
Please suggest if anyone has any ideas.
To work with list of 'resources' you can use class Resources, for example, like this:
List<EmploymentType> types = employmentTypeRepository.findAll();
Resources<Resource<EmploymentType>> resources = Resources.wrap(types);
resources.add(/* you can add some links here */);
return ResponseEntity.ok(resources);
From Resources javadoc:
General helper to easily create a wrapper for a collection of entities.
Related
I'm trying to learn spring and to achieve that i'm building a REST application from scratch. I'm confused where should I check constraints in my application: Controller layer vs. Service layer.
For example, in create user method I want to check if there is any other user with the same email, since email is unique in my database. I also want to check if password matches(password and "confirm password" fields) etc.
Currently, in my implementation, all this things are verified in Controller layer so I can return a ResponseEntity for every approach.
#PostMapping("/signUp")
public ResponseEntity<Object> createUser(#RequestBody RegisterUserDto user) {
if (userService.getUserByEmail(user.getEmailAddress()) != null) {
return ResponseEntity.badRequest().body("email already exists");
}
if (!user.getPassword().equals(user.getConfirmPassword())) {
return ResponseEntity.badRequest().body("passwords are not the same");
}
User savedUser = null;
try {
savedUser = userService.createUser(userDtoConversions.convertToEntityRegister(user));
} catch (ParseException e) {
e.printStackTrace();
}
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(savedUser.getId()).toUri();
return ResponseEntity.created(location).build();
}
Create user method in Service layer:
#Override
#Transactional
public User createUser(User newUser) {
newUser.setDateCreated(new Date());
return userRepository.save(newUser);
}
So which approach is better? If I checks constraints and validations in Service layer, what should I return so I would know in my controller why create user fails?
In my mind the best place to handle exceptions is the service layer. For my a REST controller method should, at most, handle the request and pass it over to a service method.
With this approach you have very clearly defined layers that do a very clearly defined job. For example your service layer will handle the validation of the request, the persisting action and also will provide (if needed) a return object to the controller, which then will wrap into the appropriate response object (ResponseEntity in you case).
With that in mind, there is nothing stopping you to throw any kind of exceptions in the service layer and have translated into proper responses. Spring has a very neat and powerful mechanism that does precisely that which is called an exception handler.
So in your case for the password checking action you could do something like:
if (!user.getPassword().equals(user.getConfirmPassword())) {
throw new PasswordMismatchException("Passwords are not the same for user:: " + user.getName());
}
Where the PasswordMismatchException is a RuntimeException. With something like that, you can then go ahead and setup an ExceptionHandler along with the appropriate method to intercept this and translate it into a response. A simple example would be:
#RestControllerAdvice
public class ApplicationExceptionHandler {
#ExceptionHandler(PasswordMismatchException.class)
public ResponseEntity<String> handleBadPasswords(PasswordMismatchException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
You can read up for more on this on Spring's documentation:
Spring ExceptionHandler
Exception Handling in Spring
Ok, So i've genetrated my java classes from my xsd file using jaxb. I've also written the following code as an endpoint which recieves a request (XML). Now I'd like to read the request into my java objects I can then use these to insert into my DB. Is this the correct way i should be implementing this? If so, how is it done? Thanks
#POST
#Consumes("application/xml")
#Produces("application/xml")
public String registerPost(#Context HttpServletRequest req) {
try {
//update DB
} catch (DatabaseException e) {
return "Fail";
}
}
Hopefully this helps a little :
Your jax-rs end point can accept your JAXB class directly (providing your server has been configured with a JAXB provider). It will handle the conversion from incoming text to appropiate Java objects for you.
You don't specifically need the Servlet Request Object.
You may need to convert your JAXB entities into JPA entities before you can save them to the database (or add both JAXB and JPA annotations to the same classes).
Your return type should be text/plain rather than XML (or you should return XML).
#POST
#Consumes("application/xml")
#Produces("text/plain")
public String registerPost(MyEntityClass payload) {
try {
//update DB
myService.save(payload);
return "success";
} catch (DatabaseException e) {
return "Fail";
}
}
I am checking out my project code and found below method in controller. On the net I found that controller is for receive request and provide response. Service Layer is for Business Logic and Dao layer is for data CRUD related operation.
In below method I can see business logic. Now I am not getting which code should be moved to service layer or below is fine.
I am reviewing code so I need to provide comments but I am confused.
#RequestMapping(value = "/admin/app", method = RequestMethod.POST)
public ModelAndView saveApp(
#ModelAttribute("application") #Validated Application application,
BindingResult result) {
ModelAndView model = new ModelAndView();
ApplicationFormValidator formValidation = new ApplicationFormValidator();
boolean messageFlag = false;
String operationalStatus = null;
formValidation.validate(application, result);
if (result.hasErrors()) {
model.addObject(APPLICATION, application);
model.setViewName(ADD_APP);
} else {
if(checkActive(application)){
status = FormBeanValidator.encodeStatus(application.getStatus());
application.setStatus(status);
// calling service layer and convert model into entity
messageFlag = applicationService.addApp(application);
if (messageFlag) {
Application applicationForm = new Application();
applicationForm.setSuccessMessage(PropertyHandler.getPropertyInstance().getPropertyValue(Constants.SUCCESS_MESSAGE));
model.addObject(APPLICATION, applicationForm);
model.setViewName(ADD_APP);
} else {
application.setErrorMessage(PropertyHandler.getPropertyInstance().getPropertyValue(Constants.ERROR_MESSAGE));
model.addObject(APPLICATION, application);
model.setViewName(ADD_APP);
}
}
else{
application.setErrorMessage(PropertyHandler.getPropertyInstance().getPropertyValue(Constants.OTHER));
model.addObject(APPLICATION, application);
model.setViewName(ADD_APP);
}
}
return model;
}
The code looks fine,but I would suggest some modifications:
1) Your checkActive(application) seems to check something about your business object(application),so move it to the service layer.You could merge your checkActive() method logic by moving the checkActive() method into service layer and calling it inside your applicationService.addApp(application) as a local method.
2) You have been setting the view name as same in both the if as well as else block.Try and move this code out of the if-else block as it becomes redundant.
3) It is a practice to send only the required data from the controller to the view.This is done by creating a DTO(Data Transfer Object) and making a Converter class which maps your business object fields to the DTO.You can look for example use case for DTO here
Everything is fine if you are checking for null or incorrect format validation inside your ApplicationFormValidator
I am building a simple web applications with 3 layers - DAO, Service, MVC. When in my Controller I want to delete menu group and it contains menus I am getting ConstraintViolationException.
Where should I handle this exception? In DAO, Service, or in Controller? Currently I am handling the exception in Controller.
My code below.
DAO method for deleting menu groups:
#Override
public void delete(E e){
if (e == null){
throw new DaoException("Entity can't be null.");
}
getCurrentSession().delete(e);
}
Service method for deleting menu groups:
#Override
#Transactional(readOnly = false)
public void delete(MenuGroupEntity menuGroupEntity) {
menuGroupDao.delete(menuGroupEntity);
}
Controller method for deleting menu groups in Controller:
#RequestMapping(value = "/{menuGroupId}/delete", method = RequestMethod.GET)
public ModelAndView delete(#PathVariable Long menuGroupId, RedirectAttributes redirectAttributes){
MenuGroupEntity menuGroupEntity = menuGroupService.find(menuGroupId);
if (menuGroupEntity != null){
try {
menuGroupService.delete(menuGroupEntity);
redirectAttributes.addFlashAttribute("flashMessage", "admin.menu-group-deleted");
redirectAttributes.addFlashAttribute("flashMessageType", "success");
} catch (Exception e){
redirectAttributes.addFlashAttribute("flashMessage", "admin.menu-group-could-not-be-deleted");
redirectAttributes.addFlashAttribute("flashMessageType", "danger");
}
}
return new ModelAndView("redirect:/admin/menu-group");
}
You should handle exceptions in service layer only, as part of design unless required. Think of the requirement where you need a same functionality deleteMenu for some other mapping too.
From any design point of view. Keep controller very specific to handling model attributes only serving the request mapping to business logic. Keep a method in service layer to take menuGroupId and throw exception from that service if parameter is thrown or DB error has occurred.
Refer more: Model-View-Controller, what every part really does?
I am using spring 3.2 and I have come with one requirement and can't figure out how to achieve it, first please look for below
We mostly use model in Spring MVC which is use for data binding
#ResponseBody annotation returns the string as http response
So my requirement is I want to use both together in single method base on condition, Here is my code
#RequestMapping(value="userAddEditSubmit.htm", method={RequestMethod.GET, RequestMethod.POST})
public String userAddEditSubmit(
#ModelAttribute("user") User user,
HttpServletRequest request, HttpServletResponse response, HttpSession session,
Model model
) throws Exception {
try {
//Here is my logic
return "redirect:" + url;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
So above is my method which returns specific jsp with model attribute, but now in one condition I have requirement to return String data instead of whole jsp in the same method, what can I do to achieve this? Any help will be highly appreciated.
You can simply return null from that method when your condition is met and write to the response yourself. Spring assumes that when a method returns null it has handled the response itself.