Spring : Controller and Service Layer coding standard example - java

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

Related

Constraints check controller layer vs service layer

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

Custom Spring Data Rest Controller with HATEOAS Support

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.

Spring MVC + Thymeleaf: passing objects through pages

I'm developing a simple web application using spring MVC and thymeleaf. I have a form correctly handled by this method in my controller
#RequestMapping(value = "/list", method = RequestMethod.POST)
public ModelAndView searchJob(
#ModelAttribute(SEARCH_PARAMS) #Valid SearchParams params,
BindingResult bindingResult) {
ModelAndView output = null;
if (!bindingResult.hasErrors()) {
JobsAPIImplService service = new JobsAPIImplService();
JobsAPI api = service.getJobsAPIImplPort();
ArrayList<NoaJob> jobs = (ArrayList<NoaJob>) (api.search(JobUtils.toSearchParams(params))).getItem();
output = new ModelAndView("scheduler/list");
output.addObject("jobs", jobs);
} else {
// errors handling
}
return output;
}
So in my result page I can access to the ArrayList "jobs" in this way:
<tr th:each="job : ${jobs}">
...
</tr>
In the same page, I have a simple link which calls another GET method on the same controller. The goal here is to have the same ArrayList in another page in order to implement a "back" button without re-executing the search logic (a call to a web service).
Here is the method called
#RequestMapping(value="/list/{id}", method = RequestMethod.GET)
public ModelAndView details(#PathVariable("id") String jobUuid,
#ModelAttribute("jobs") ArrayList<NoaJob> jobs) {
ModelAndView output = new ModelAndView("scheduler/details");
LOGGER.info("Size jobs list: " + jobs.size());
NoaJob job = new NoaJob();
job.setJobUuid(jobUuid);
output.addObject("job", job);
output.addObject("jobs", jobs);
return output;
}
the problem is that the arraylist here is always null! I read that in GET requests Spring allocates a new ModelAttribute, so how can I pass this object throug pages?
Define a session attribute like this in the head of your controller:
#SessionAttributes({ "myFancyList"})
#Controller
public class HomeController {
// your code here
}
Now.. when you have to insert the "list" to be viewable via thymeleaf:
output.addObject("myFancyList", jobs);
and modify thymleaf pseudocode accordingly.
in the "post" of the search "override" the session attribute with the current search result..
i think this should do the trick for you

Exception handling in Spring MVC with 3 layer architecture

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?

Java Restful Web Services (jax rs) authentication pattern

I have started using JAX-RS to created a simple restful interface to my web application. Currently, it is only being used (read only) by one internal client which has access to all application data, and I am using http basic authentication for access. I would like to start using it as part of the view layer of my app, and certain operations will only be allowed if a user is logged in via the web app. I am struggling to find a pattern that allows me to use both forms of authentication in an elegant way, without repeating a lot of code. Here is roughly what I have come up with:
First a util class for loading an application session, which is stored in the database.
public class RestUtil {
public static AppSession getAuthenticatedSession(HttpServletRequest request) {
AppSession session;
String remoteUser = request.getRemoteUser();
if (remoteUser != null) {
session = SessionRepository.loadSessionByRemoteUser(remoteUser);
} else {
session = SessionRepository.loadSessionById(request.getSession().getId());
}
return session;
}
}
Here's our resource, with one method that is only accessible to an authenticated user, or our http basic auth client:
#Path("/protected/resource")
public class ProtectedResource {
#GET
#Produces(MediaType.TEXT_JSON)
#Path("{userId}")
public String getProtectedResourceJson(#Context HttpServletRequest request, #PathParam("userId") Integer userId) {
// Return Charity List XML
AppSession session = RestUtil.getAuthenticatedSession(request);
if (session.canAccessUser(userId)) //get Json...
}
}
Here's the most basic view of the AppSession, for the purpose of this question:
public class AppSession {
User authenticatedUser;
String remoteUser;
public boolean canAccessUser(Integer userId) {
if (remoteUser != null) {
//this client has access to all users
return true;
} else if (authenticatedUser.getId().equals(userId)) {
//this is local client, calling the service from a view
//only has access to authenticatedUser
return true;
} else {
return false;
}
}
}
Furthermore, for services that do not require any sort of authentication, how do I prevent unauthorized third parties from just pointing at the url, and grabbing the data at their leisure?
You're getting to the point when it is worth looking into using aspect-oriented programming to split the security side of things from your business logic. If you're already using Spring to assemble the pieces of your app (which I recommend for complex servers) then it's just a matter of adding in Spring AOP to inject the security logic. Otherwise, use AspectJ directly. The actual logic to handle the multiple login modes will probably have to be custom, but at least you can keep it quarantined.
If using Spring, consider using Spring Security; that builds on top of Spring AOP and supplies you with much more of the solution.

Categories

Resources