I have a method of downloading messages in the controller
#GetMapping(value = "/sent/{id}")
public
HttpEntity<MessageSent> getMessageSent(
#ApiParam(value = "The message ID", required = true) #PathVariable Long id
) {
return ResponseEntity.ok().body(messageSearchService.getMessageSent(id, authorizationService.getUserId()));
}
However, I have forgotten to verify if the message about the given ID belongs to the user. It does not do this either in the service.
#Override
public MessageSent getMessageSent(
#Min(1) Long messageId,
#Min(1) Long userId
) throws ResourceNotFoundException {
Optional<UserEntity> user = this.userRepository.findByIdAndEnabledTrue(userId);
user.orElseThrow(() -> new ResourceNotFoundException("No user found with id " + userId));
return this.messageRepository.findByIdAndSenderAndIsVisibleForSenderTrue(messageId, user.get())
.map(MessageEntity::getSentDTO)
.orElseThrow(() -> new ResourceNotFoundException("No message found with id " + messageId));
}
And now my question is whether it should be done in the controller or service? I would prefer to do this in the service, but I don't know if it is appropriate.
As a general rule of thumb, I would say that business logic of this sort should be in the service. Controllers should be light-weight and pass on requests. Further, there may be other clients of your service, not just controllers, so this allows you to keep validation in one place.
Don't put this validation in the controller - controllers part is only be entry points for coming requests and exposing API's.
I would suggest you to create additional service which responsible of doing the validation and injecting this service in your
messageSearchService service. This way you can use the validation
service in other services as well which required same validation. In
Addition , this why you follow the principle of each class has only
one its own responsibility.
Related
Today i have a big discussion with one of our team member about validation of RESTful API input in Controller and Service layer and i feel it is a bad day for making bigger argument. So we have a spring boot microservice application with layered architecture
Controller --> Service --> Repository
The argument is having the validation at each layer or just having the validation at Controller layer, for example we have Controller with POST request and using JSR-380 validation for input request body
Controller :
#PostMapping(value = "/", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<Detail> createConfig(
#NotNull(message = "{error.message.config_detail}")
#RequestBody #Validated Config config) {
return new ResponseEntity<>(configService.create(config.getConfigId(), config.getTaskId()), HttpStatus.CREATED);
}
Config : Request Body
public class Config {
#ApiModelProperty(name = "config_id", example = "1", required = true)
#NotNull(message = "{error.message.config_id}")
private Long configId;
#ApiModelProperty(name = "task_id", example = "11", required = true)
#NotNull(message = "{error.message.task_id}")
#Min(value = 0, message = "{error.message.task_id}")
#Max(value = 9999, message = "{error.message.task_id}")
private Integer taskId;
// bunch of addition fields with validations
}
If the validation success then calling the Service method with some properties from Config
Service :
public Detail create(#Valid #NotNull Long configId, #NotNull Integer taskId) {
// some business logic to convert to entity and saving to database
return repository.save(entity));
}
So if we see the above code, same validation is done at Controller and Service, So i argued that there is no need of validating in Service layer, perform the validation at controller layer and if input is wrong then throw 400 or 500 to user. But another person in team also suggest having validation in each block for whatever is used in the block, so that individual piece of code is safe (focusing on the unit instead of the integration path).
I know i might be wrong in this case, but still not able to understand the validation at each layer,(null check i agree) so it is recommended approach having validation at each level
Controller --> validation call service
Service ---> validation and call business
Business ---> validation and call repository
Repository --> save
But what is the prefered way of validation ? according to me if Controller input is valid call Service and perform business logic and call Repository. correct me if i'm wrong so that i can follow the recommended pattern
You are right : the validation should be placed in controller, if possible. And it's make no sense in my opinion to validate the data more then 1 time.
See also DRY principle https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
It is situational. Some validation needs to be performed in the service layer. Say, you will need to change a state of an object, but only if certain conditions are met etc.
A part from that, as a rule of thumb i follow these guidelines:
If your services are publicly exposed to other services via RMI, then service layer validation is mandatory, if not go with controller level validation.
Unless you are using the services and / or repositories as libraries in other projects, too, it does not make sense to do the validation multiple times.
If the controllers are the only classes accessing the additional layers there is no need for additional validation as you and your controller are the only one to access the Spring beans. You and the controller are in control of accessing the services with valid parameters.
Doing several validations has the disadvantage of writing more complex services - including unit tests.
It is slower.
Changing the validation will take longer as multiple layers have to be changed - including unit tests.
The validations on several layers can and will differ in a matter of time.
The preferred way is to do the validation once in (or even before) the controller and writing integration tests to ensure its functionality.
I am developing a restful web service using spring framework. I have a few questions on how to use DTOs properly in my project.
First of all, I saw a lot of code examples on using DTOs and there is always a one to one mapping with entities. For User entity we have UserDto, For Country, we have CountryDto and etc. I think this is not flexible at all. For every service, I need two DTOs for input and output. is that ok?
If I define two DTOs for each service, Can I name them with request and response suffixes? For example for createUser service, I have two DTO objects called CreateUserRequest and CreateUserResponse.
Some services have no outputs(Just a status code) or inputs(Just an Id in URL). Should I Define an empty DTO for them or not? If I don't define a DTO in these scenarios, it is a bit confusing because sometimes you define two DTOs for service and sometimes just one.
Should I map path variables like /users/{id} to DTO objects too? for example instead of:
#GetMapping("/users/{id}")
public ResponseEntity getUser(#PathVariable(value = "id") Long id)
do something like:
#GetMapping("/users/{id}")
public ResponseEntity getUser(#PathVariable(value = "id") GetUserRequest request)
Follow this pattern :
#RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getUser(#PathVariable long id) {
ResponseEntity<User> response = null;
try {
User user = userService.getUser(id);
response = new ResponseEntity<User>(user, HttpStatus.OK);
} catch (ApplicationException ex) {
response = new ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
return response;
}
Don't create a url like this : /users/{id}
Create a url like this - /{id} - this will give more abstraction.
In the ideal restful web service you manipulate resources with properties, not request-response structures. So, if you create User, it's ok to accept and return the same DTO and the method looks like this:
#PostMapping("/")
public ResponseEntity<User> addUser(#RequestBody User user)
Using request body for GET is technically possible, but de-facto is deprecated, use path variables and request params.
Some services have no outputs(Just a status code) or inputs(Just an Id in URL). Should I Define an empty DTO for them or not?
No, you should not define DTO for objects that you do not have.
And the last detail: if you have multiple methods manipulating same resource (e.g. User), do not make multiple User DTOs for get, post, etc.
I am currently developing an API where I'm using DTO for the first time. So far I've used Spring's form validation with javax.validation.
So my question is if there is a way to combine both DTO and "form" validation. Let me explain myself: lets say I have a service to log in and another to register. In the service to register we have: name, password and email, the 3 of them must be filled. As for the login service, only the email and password must be filled. So we'd have something like:
private String name;
private String password;
private String email;
Until now, what I did was to create a POJO per request (forms) and then use annotations such as #NotNull but now with DTO in the project I'm in now they just have the same DTO and business object with the same properties and no constraints.
How could I do what I was usually doing? Checking the fields that must be not null in the controller looks a little dirty to me and I can't just put something like #NotNull in the UserDTO because then in the two examples I said I'd have to send also the name when logging in although it's not needed for that service.
So, how could I combine these 2 things? Is this something not possible or there's a better approach?
Thanks.
I assume you are using two separate controllers for login and register requests.
And if it is the case, then you can make good use of org.springframework.validation.Validator interface:
#Component("registrationValidator")
public class RegistrationValidatorImpl implements Validator {
#Override
public boolean supports(final Class<?> aClass) {
}
#Override
public void validate(final Object o, final Errors errors) {
}
}
Create RegistrationValidatorImpl and LoginValidatorIml and #Autowire it in your controllers.
The usage of validator is simple:
invokeValidator(registrationValidator, someDTO, errors);
if (errors.hasErrors()) {
return new ResponseEntity(HttpStatus.BAD_REQUEST); //or whatever logic here
}
The controller method signature should be similar to this:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity register(#RequestBody final SomeDTO someDTO, final HttpServletRequest request, final Errors errors) {}
I case of one controller, I assume you have different methods mapped to login and register requests. You can #Autowire both validators in controller and use each in separate methods.
Using groups for validation with javax.validation did the work. I followed the answer in this question (as Andrew suggested), then I just had to put every field I wanted to have different rules in different groups.
How can I allow only a one request to micorservice method with specific URL #PathVariable per User.
My Controller
#RestController
#RequestMapping(value = "/rest/product", produces = "application/json;charset=UTF-8")
public class ProductRestController {
#Autowired
ProductService productService;
#Autowired
ProductAsm productAsm;
#RequestMapping(value = "/ID/{ID}", method = RequestMethod.GET)
public ResponseEntity<ProductResource> getProductID(#PathVariable("ID") Long ID, #AuthenticationPrincipal User) {
Product product = productService.getProduct(ID);
if (product == null)
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
return new ResponseEntity<>(productAsm.toResource(product), HttpStatus.OK);
}
For example :
first request /rest/product/ID/2231 allowed for USER(with login="xaxa" )
second request /rest/product/ID/2545 allowed for USER(with login="xaxa" )
thirdth request /rest/product/ID/2231 not allowed for USER(with login="xaxa" )
Which is the best way to implement this functionality?(Have I to keep this URL request with User login in DB or there is already solutions)
You could use AOP and implement your own aspect that would be called Before your Rest Endpoint method.
This pointcut would read ID provided in a request and would try to find a Lock corresponding with this ID. Then the usual - try to access resource and potentially wait.
Implementation could base on Guava's Striped class - at least for the start.
There are several problems that need to be taken into consideration:
Striped could be replaced with some LRU Cache for better memory management.
You would of course have to provide synchronization for the case when the same ID is accessed simultaneously by two requests.
It would work only for an application deployed on a single node.
It would not be very good approach performance-wise. Depending on your traffic this may be an issue.
I expose a Spring REST service like this..
#RequestMapping(value = "/{id}", method = RequestMethod.GET, headers = Constant.ACCEPT_APPLICATION_JSON)
#ResponseBody
public IndividualProviderDto showJson(#PathVariable("id") Long id) {
IndividualProviderDto individualProviderDto = individualProviderService.findIndividualProvider(id);
if (SecurityUtils.getCurrentLogin().equals(individualProviderDto.getUserName())) {
individualProviderDto.setCredential("");
HttpHeaders headers = new HttpHeaders();
headers.add(Constant.CONTENT_TYPE, Constant.APPLICATION_JSON_CHARSET_UTF8);
return individualProviderDto;
}
throw new IllegalArgumentException("User not found");
}
In the above code I am explicitly making a check to ensure that the id belongs to the logged in USER.
SecurityUtils.getCurrentLogin().equals(individualProviderDto.getUserName()
This check has to be applied wherever I need to protect a resource. Of course I can have an Aspect and apply it from one place using pointcut expressions. I have also heard of ESAPI that forges the url
but I was wondering if Spring Security Configuration provides something out of the box, so that I don't reinvent the wheel.
Spring security is not the answer -- it provides facility for authentication and authorization. Checking availability of a particular entity (in your case id) should be a part of service layer, as it's already implemented in your code.