Consider following UserDTO class and UserController exposing endpoints to create, update and get User.
Having the id property in the UserDTO class does not make sense for create and update. If I use swagger or another auto generated API documentation then it shows that the id can be passed in create end point. But the system does not use it as ids are generated internally.
If I look at get then probably I can get rid of the id property but it is certainly required in a list user end point.
I was thinking of returning internal User domain object in get/list end points. This way I can then get rid of id property form UserDTO class.
Is there any better option I can employ for this?
public class UserDTO {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#RestController
#RequestMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Void> create(#RequestBody UserDTO user) {
}
#RequestMapping(value = "{id}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<UserDTO> get(#PathVariable("id") int id) {
}
#RequestMapping(value = "{id}", method = RequestMethod.PUT)
#ResponseBody
public ResponseEntity<Void> update(#PathVariable("id") int id, #RequestBody UserDTO user) {
}
}
This question may have been asked but I could not find. So excuse me for duplicate question.
Data Transfer Object (DTO) is a pattern that was created with a very well defined purpose: transfer data to remote interfaces, just like web services. This pattern fits very well in REST APIs and DTOs will give you more flexibility in the long run.
I would recommend using tailored classes for your endpoints, once REST resource representations don't need to have the same attributes as the persistence objects.
To avoid boilerplate code, you can use mapping frameworks such as MapStruct to map your REST API DTOs from/to your persistence objects.
For details on the benefits of using DTOs in REST APIs, check the following answers:
Why you should use DTOs in your REST API
Using tailored classes of request and response
To give your DTOs better names, check the following answer:
Giving meaningful names to your DTOs
What's about creating two different interfaces :
interface UserDTO {
public String getName ();
public void setName (String name);
}
interface IdentifiableUserDTO extends UserDTO {
public Long getId ();
public void setId (Long id);
}
class DefaultUserDTO implements IdentifiableUserDTO {
}
and then use the Interface in your controller instead of the DTO class :
#RestController
#RequestMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Void> create(#RequestBody IdentifiableUserDTO user) {
}
#RequestMapping(value = "{id}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<UserDTO> get(#PathVariable("id") int id) {
}
#RequestMapping(value = "{id}", method = RequestMethod.PUT)
#ResponseBody
public ResponseEntity<Void> update(#PathVariable("id") int id, #RequestBody UserDTO user) {
}
}
Related
I'm using spring-data-rest to expose a database content via a spring servlet. Using #QuerydslPredicate for being able to send a filter via HTTP GET, like:
localhost:8080/persons?firstname=john&lastname=doe
Question: is it possible to tell the Predicate validation rules? Eg that the specific fields may not be null, eg lastname?
#RestController
public class PersonServlet {
#GetMapping("/persons")
public Iterable<Person> getPersons(
#QuerydslPredicate(root = Person.class) com.querydsl.core.types.Predicate predicate,
Pageable pageable) {
return dao.findAll(predicate, pageable);
}
#Entity
public class Person {
private String firstname;
private String lastname;
private String age;
//many more fields
}
}
There seems to be no way validating eg QPerson.lastname != null.
So I used the following approach adding #Valid Person as get parameter and adding validation constraints like #NotNull.
#RestController
public class PersonServlet {
#GetMapping("/persons")
public Iterable<Person> getPersons(
#QuerydslPredicate(root = Person.class) com.querydsl.core.types.Predicate predicate,
Pageable pageable,
#Valid Person p) {
return dao.findAll(predicate, pageable);
}
#Entity
public class Person {
private String firstname;
#NotNull
private String lastname;
private String age;
//many more fields
}
}
How can I have an interface as a ModelAttribute as in the below scenario?
#GetMapping("/{id}")
public String get(#PathVariable String id, ModelMap map) {
map.put("entity", service.getById(id));
return "view";
}
#PostMapping("/{id}")
public String update(#ModelAttribute("entity") Entity entity) {
service.store(entity);
return "view";
}
Above snippet gives the follow errors
BeanInstantiationException: Failed to instantiate [foo.Entity]: Specified class is an interface
I don't want spring to instantiate entity for me, I want to use the existing instance provided by map.put("entity", ..).
As been pointed out in comments, the Entity instance does not survive between the get and post requests.
The solution is this
#ModelAttribute("entity")
public Entity entity(#PathVariable String id) {
return service.getById(id);
}
#GetMapping("/{id}")
public String get() {
return "view";
}
#PostMapping("/{id})
public String update(#ModelAttribute("entity") Entity entity) {
service.store(entity);
return "view";
}
What happens here is that the Entity in update binds to the Entity created from the #ModelAttribute annotated entity method. Spring then applies the form-values to the existing object.
I am using spring 4 REST.
I have a base class and many other class extends the same.
For example, Employee is the base class and other classes hr, engineer, trainer etc etc extends the employee.
I have to create REST API to create the different type of employee.
The interface is one POST which accepts all the type of employees. I cannot create different interface for each sub type. From the base, I know what is the sub type.
#RequestMapping(value= "/test/{employeeType}", method = RequestMethod.POST)
public void createEmp(#RequestBody Employee employee){
//If type is HR, I want to cast to HR type
//Is there any way we can take generic object in spring rest and then manage internally ?
}
Maybe try this?
#RequestMapping(value= "/test/{employeeType}", method = RequestMethod.POST)
public void createEmp(#PathVariable String employeeType, #RequestBody EmployeeDTO employeeDTO){
transform(employeeType,employeeDTO);
}
Here EmployeeDTO will contain all possible parameters so it can construct any of the child classes then based on the employeeType you just transform into domain object(Employee)?
Edit2 as requested
Here is sample code:
public class Employee {
private String name;
}
public class Hr extends Employee {
private String department;
}
public class Hr extends Employee {
private String department;
}
Then the DTO class should look like this:
public class EmployeeDTO {
private String name;
private String course;
private String department;
}
Then when you know your type you transform to whatever your want with all the necessary values in the DTO
Edit: Now when I think about it this also may be an option but I will need to see your classes.
#RequestMapping(value= "/test/employee", method = RequestMethod.POST)
public void createEmp(#RequestBody Employee employee){
#RequestMapping(value= "/test/hr", method = RequestMethod.POST)
public void createHr(#RequestBody Hr hr){
Need to see the search result of the:
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
public User findByUsername(String username);
}
which I call from my controller:
#RequestMapping(value = "/users/find", method = RequestMethod.GET)
public #ResponseBody User findByUsername(#RequestParam("username") String userName) {
return usersRepo.findByUsername(userName);
}
The method above is called via:
#GET("/users/find")
public User findByUsername(String userName);
And the class User is here:
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String username;
etc...
Results of the default methods I can see in my browser by typing e.g. localhost:8080/users
I tried many possible requests but it seems that I'm missing something. Any help is appreciated.
Try this:
Here you will get plane User object on browser so try to change return type to String.
#RequestMapping(value = "/users/find", method = RequestMethod.GET)
#ResponseBody
public User findByUsername(#RequestParam("username") String username) {
return usersRepo.findByUsername(username);
}
Hit this URL from local machine
localhost:8080/users/find?username=99
Now I recognize that my question was about testing an API. The best solution for it is to use Postman. The tool is really easy to learn and request results are in a better form.
In my Spring MVC webapp I have a generic RESTful controller for CRUD operations. And each concrete controller had to declare only a #RequestMapping, for example /foo. Generic controller handled all request to /foo and /foo/{id}.
But now I need to write a bit more complex CRUD controller which will get additional request params or path variables, e.g /foo/{date} and /foo/{id}/{date}. So I extend my generic CRUD controller and write overloaded fetch(id, date) method which will deal with both {id} and {date}. That is not a problem.
But I also need to 'disable' fetch(id) implementation derived from base class (resource mustn't be available at /foo/{id} anymore, only at /foo/{id}/{date}). The only idea I came up with is to override this method in my concrete controller, to map it on a fake uri and return null. But this looks like rather ugly dirty hack because we expose some fake resource uri, instead of disabling it. May be there is a better practice?
Any ideas?
//My generic CRUD controller
public abstract class AbstractCRUDControllerBean<E, PK extends Serializable> implements AbstractCRUDController<E, PK> {
#RequestMapping(method=RequestMethod.GET)
public #ResponseBody ResponseEntity<E[]> fetchAll() { ... }
#RequestMapping(value="/{id}", method=RequestMethod.GET)
public #ResponseBody ResponseEntity<E> fetch(#PathVariable("id") PK id) { ... }
#RequestMapping(method=RequestMethod.POST)
public #ResponseBody ResponseEntity<E> add(#RequestBody E entity) { ... }
#RequestMapping(value="/{id}", method=RequestMethod.PUT)
public #ResponseBody ResponseEntity<E> update(#PathVariable("id") PK id, #RequestBody E entity) { ... }
#RequestMapping(value="/{id}", method=RequestMethod.DELETE)
public #ResponseBody ResponseEntity<E> remove(#PathVariable("id") PK id) { .. }
}
.
//Concrete controller, working with Foo entities
#Controller
#RequestMapping("/foo")
public class FooControllerImpl extends
AbstractCRUDControllerBean<Foo, Long> implements FooController {
//ugly overriding parent's method
#RequestMapping(value="/null",method=RequestMethod.GET)
public #ResponseBody ResponseEntity<Foo> fetch(#PathVariable("id") PK id) {
return null;
}
//new fetch implementation
#RequestMapping(value="/{id}/{date}", method=RequestMethod.GET)
public #ResponseBody ResponseEntity<Foo> fetch(#PathVariable("id") PK id, #PathVariable("date") Date date) { .... }
}
Are you trying to achieve the resource, subresource type of jersey using spring? That may not be directly possible. Instead of declaring the generic RESTful service as controller, why don't you delegate it to them?
//My generic CRUD Operations
public abstract class AbstractCRUDControllerBean<E, PK extends Serializable> implements AbstractCRUDController<E, PK> {
public ResponseEntity<E[]> fetchAll() { ... }
public ResponseEntity<E> fetch(#PathVariable("id") PK id) { ... }
public ResponseEntity<E> add(#RequestBody E entity) { ... }
public ResponseEntity<E> update(#PathVariable("id") PK id, #RequestBody E entity) { ... }
public ResponseEntity<E> remove(#PathVariable("id") PK id) { .. }
}
and delegate in the controller.
//Concrete controller, working with Foo entities
#Controller
#RequestMapping("/foo")
public class FooControllerImpl extends
AbstractCRUDControllerBean<Foo, Long> implements FooController {
//we are interested in using fetchall but not others
#RequestMapping(method=RequestMethod.GET)
public #ResponseBody ResponseEntity<Foo> fetch(#PathVariable("id") PK id) {
return fetchAll();
}
//fetch with id and date
#RequestMapping(value="/{id}/{date}", method=RequestMethod.GET)
public #ResponseBody ResponseEntity<Foo> fetch(#PathVariable("id") PK id, #PathVariable("date") Date date) { .... }
}
also, you can map method based on the availability of the parameters too,
#RequestMapping(value="/{id}/{date}", params={"param1","param2","!param3"})
public #ResponseBody ResponseEntity<E> customFetch(#PathVariable("id") PK id,
#PathVariable("date") Date date, #RequestParam("param1") String param1,
#RequestParam("param2") String param2) {...}
This method maps /foo/id/date when param1 and param2 exists and param3 does not exist.