How is a custom derived query POST added to spring data REST? - spring

I have a Spring data REST project I am using to learn.
Right now I want to set up a query in this repository:
#RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepository extends PagingAndSortingRepository<User, Long>
namely, this guy:
#RestResource(path = "login", rel = "login")
User findByUsernameAndPassword(String username, String password);
A basic repository is easy to set up. So are custom GET requests like:
List<Item> findByType(#Param("type") String type);
or
#RestResource(path = "byMaxPrice")
#Query("SELECT i FROM Item i WHERE i.price <= :maxPrice")
List<Item> findItemsLessThan(#Param("maxPrice") double maxPrice);
But these are still GET requests. I would like to use a POST request. The method = RequestMapping.POST markup isn't accepted by #RestResource .. and I dont see any mention of different request types in the documentation. how do I do this?

You need to define a custom Controller to handle POST requests. If you just want to do POST for the default Repository methods, unfortunately, you still have to make a pass through Controller
#RepositoryRestController
public class MyController implements Serializable {
...
#RequestMapping(value = "/myConstroller/myPostMethod", method = RequestMethod.POST
, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody List<MyObjects> updateMyObjectList(
#RequestBody List<MyObjects> objects) {
// Call your repository method here, or a custom service, or whatever
}
See Spring MVC docs for further information, specifically section 5.2.1, which describes the default HTTP Method support for Repositories, and 16.4, which gives a custom Controller example.
From 5.2.1:
POST
Creates a new entity from the given request body.
Thus, POST is supported, but not to do what you are trying to do. If you want to "hide" URL parameters by using POST instead of GET, you need a custom Controller.

Related

Spring RESTful application - POST method request body mandatory attributes

I am building a RESTful app in Spring Boot and i want to make few attributes in my POST method's request body mandatory.
In swagger yaml, i mark them as required "true", but when i generate the classes using swagger editor, i dont see that impacting in any way, i.e i can't see even a #NotNull annotation or anything of that sort.
How do i mark them as mandatory in my java model class ? Is #NotNull the way to go?
If yes, should i do that in my request body class, or in the jpa document class or both ?
Thanks !
Yes, #NotNull is a way to go.
But also You need to use #Valid annotation.
check example:
#RequestMapping(value = "/appointments", method = RequestMethod.POST)
public String add(#Valid AppointmentForm form, BindingResult result) {
....
}
static class AppointmentForm {
#NotNull
private Date date;
}

Map #CookieValue, #RequestHeader etc. to POJO in Spring Controller

I have a bunch of params in my controller and want to map all of them to a separate POJO to keep readability. There is also a #CookieValue, #RequestHeader I need to evaluate and aim for a solution to also map them to that POJO. But how?
I saw a possible solution on a blog but it doesn't work, the variable stays null.
Controller:
#RequestMapping(path = MAPPING_LANGUAGE + "/category", produces = MediaType.TEXT_HTML_VALUE)
#ResponseBody
public String category(CategoryViewResolveModel model) {
doSomething();
}
And my POJO is this:
public class CategoryViewResolveModel {
private String pageLayoutCookieValue;
public CategoryViewResolveModel() {
}
public CategoryViewResolveModel(
#CookieValue(value = "SOME_COOKIE", required = false) String pageLayoutCookieValue) {
this.pageLayoutCookieValue = pageLayoutCookieValue;
}
... some other RequestParams, PathVariables etc.
}
According to the documentation it's not possible for #CookieValue and #RequestHeader.
This annotation is supported for annotated handler methods in Servlet
and Portlet environments.
Take a look at:
https://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-creating-a-custom-handlermethodargumentresolver/
instead of using getParameter to access request parameters you can use getHeader to retrieve the header value and so define your CategoryViewResolveModel just as you were requesting

Return custom-typed object from JpaRepository

I have the following repository:
public interface UserRepository extends BaseDAO<User> {
Collection<User> findByEmail(#Param("email") String email);
#Query("select new com.data.CustomUser(upper(substring(u.lastName, 1, 1)) as initial, count(*)) from User u join u.chats c where c.business=:business group by upper(substring(u.lastName, 1, 1)) order by initial")
List<CustomUser> getContactsIndex(#Param("email") String email);
}
which is exposed with Spring Data REST. The User object is a managed entity, while CustomUser not and as you can see, it's build on-fly by using custom query.
Once I want to call that function, it fails with Persistent entity must not be a null! exception. Is there any way to implement this behavior?
P.S. Expose CustomUser with separate repository is impossible because it is not a managed entity.
One challenge with using Spring Data Rest is when you hit an edge case and you don't know whether you've hit a bug or whether you're just outside the scope of what the library is intended for. In this case I think you are at the edge of what SDR will easily do for you, and it's time to implement your own controller.
Spring Data Rest is looking for an Entity - in your case a User - as the return type for ALL methods in the repository to expose under /entities/search, and breaks when it doesn't find that entity type. The User it wants to serialize isn't there, hence the "Persistent entity must not be null".
The way around this is to write a simple #Controller that has a #RequestMapping for the exact same url exposed by the repository method. This will override the SDR generated implementation for that url, and from that you can return whatever you want.
Your implementation might look something like this:
#Controller
public class CustomUserController {
private final UserRepository repository;
#Inject
public CustomUserController(UserRepository repo) {
repository = repo;
}
#RequestMapping(value = "/users/search/getContactsIndex", method = GET, produces = {MediaType.APPLICATION_JSON_VALUE})
public #ResponseBody List<CustomUser> getContactsIndex(#RequestParam String email) {
return repository.getContactsIndex(email);
}
}
Be aware that there is a "recommended" way to override functionality this way. There is an open issue to document the best way to do this.

How to map Multiple controllers in Spring MVC

I have two controllers in my Application; one is userController, where I have add, delete and update methods; the other one is studentController, where I also have add, delete and update methods.
All the mappings are same in my methods using #RequestMapping annotation in both controllers. I have one confusion: if we are passing the same action from the JSP, then how will the Dispatcher find the corresponding controller? If anybody could describe this using example will be appreciated.
You have to set a #RequestMapping annotation at the class level the value of that annotation will be the prefix of all requests coming to that controller,
for example:
you can have a user controller
#Controller
#RequestMapping("user")
public class UserController {
#RequestMapping("edit")
public ModelAndView edit(#RequestParam(value = "id", required = false) Long id, Map<String, Object> model) {
...
}
}
and a student controller
#Controller
#RequestMapping("student")
public class StudentController {
#RequestMapping("edit")
public ModelAndView edit(#RequestParam(value = "id", required = false) Long id, Map<String, Object> model) {
...
}
}
Both controller have the same method, with same request mapping but you can access them via following uris:
yourserver/user/edit
yourserver/student/edit
hth
We can have any number of controllers, the URL mapping will decide which controller to call..
Please refer here for detailed Spring MVC multiple Controller example

Spring MVC to Spring REST tutorials misunderstanding

I have developed a Spring MVC - Hibernate application as told here.
Now I am trying to modify this code to create a REST application as told here.
I have added Jackson library to the classpath and added #XmlRootElement.
#XmlRootElement(name = "persons")
public class Person implements Serializable {
But if I do a application/json request then I still get the html code back.
What I am doing wrong / forgot to do?
My controller:
#RequestMapping(value = "/persons", method = RequestMethod.GET)
#ResponseBody
public String getPersons(Model model) {
logger.info("Received request to show all persons");
// Retrieve all persons by delegating the call to PersonService
List<Person> persons = personService.getAll();
model.addAttribute("persons", persons);
return "personspage";
}
Changed the Controller, but get an error:
t
ype Status report
message /Buddies/WEB-INF/jsp/main/persons/1.jsp
description The requested resource (/Buddies/WEB-INF/jsp/main/persons/1.jsp) is not available.
Your controller should look like this:
#RequestMapping(value = "/persons/{id}", method = RequestMethod.GET)
#ResponseBody
public Person getPerson(#PathVariable int id) {
Person person = personService.getPersonById(id);
return person;
}
If you want to return a list of Person objects, you need an extra wrapper object, see: Using JAXB to unmarshal/marshal a List<String>.
You are probably missing AnnotationMethodHandlerAdapter and messageConverter in your spring configuration.

Resources