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.
Related
Basically, I'm interested whether it's intended that the only models Swagger shows in swagger-ui are models used in RestController methods. It detects both my DTOs that I filled with #RequestBody, but it does not detect the User model, even with the ApiModel annotation. How to I go around this without making a dummy controller method?
For example:
#PostMapping("/signin")
#ApiOperation
public String login(
#ApiParam(value = "The login credentials DTO (username and password)", required = true)
#RequestBody
#Valid LoginCredentialsDTO loginCredentialsDTO) {
return userService.login(loginCredentialsDTO);
}
It detects the Model "LoginCredentialsDTO" because it was used here in the controller method.
Since I only use DTOs in my controller, it's not detecting my main model (User). I don't want to have to make a dummy method just for Swagger to be able to detect all my models.
Swagger describes the external interface of your api. When your User model is not used externally is will not be visible. See also swagger.io/docs/specification/2-0/basic-structure
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.
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.
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.
In Spring MVC I have a controller that listens to all requests coming to /my/app/path/controller/*.
Let's say a request comes to /my/app/path/controller/blah/blah/blah/1/2/3.
How do I get the /blah/blah/blah/1/2/3 part, i.e. the part that matches the * in the handler mapping definition.
In other words, I am looking for something similar that pathInfo does for servlets but for controllers.
In Spring 3 you can use the # PathVariable annotation to grab parts of the URL.
Here's a quick example from http://blog.springsource.com/2009/03/08/rest-in-spring-3-mvc/
#RequestMapping(value="/hotels/{hotel}/bookings/{booking}", method=RequestMethod.GET)
public String getBooking(#PathVariable("hotel") long hotelId, #PathVariable("booking") long bookingId, Model model) {
Hotel hotel = hotelService.getHotel(hotelId);
Booking booking = hotel.getBooking(bookingId);
model.addAttribute("booking", booking);
return "booking";
}
In Spring 2.5 you can override any method that takes an instance of HttpServletRequest as an argument.
org.springframework.web.servlet.mvc.AbstractController.handleRequest
In Spring 3 you can add a HttpServletRequest argument to your controller method and spring will automatically bind the request to it.
e.g.
#RequestMapping(method = RequestMethod.GET)
public ModelMap doSomething( HttpServletRequest request) { ... }
In either case, this object is the same request object you work with in a servlet, including the getPathInfo method.