Spring MVC + JSON in #ResponseBody + inheritance - ajax

I have a class hierarchy. On the top of it there is a an abstract AnswerUnit class. There are two inheriting classes: OpenQuestionAnswer and MultipleChoiceQuestionAnswer.
I have a .jsp form that sends data (object serialized to JSON) to server with AJAX request and a method in controller to handle it.
#RequestMapping(value = "/test", method = RequestMethod.POST)
public #ResponseBody
String testPostMethod(#RequestBody
OpenQuestionAnswer answer) {
return "home";
}
I would like to be able to take "AnswerUnit answer" as the argument (abstract type instead of concrete type, so I could handle request from different views with one method). When I try to do it there is a problem - server respose is
400 BAD REQUEST he request sent by the client was syntactically incorrect.
I think that the reason is that Spring (Jackson?) isn't able to find out which concrete class he should create and use.
On the client side I know what type of class I send to server.
What is the proper way to tell server which concrete class should be created and filled with my request?

I guess I'm late with response, but anyway:)
http://wiki.fasterxml.com/JacksonAnnotations
You can have this using Jackson Polymorphic type handling
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes(value = {
#JsonSubTypes.Type(name = "answer", value = OpenQuestionAnswer.class),
#JsonSubTypes.Type(name = "multiple", value = MultipleChoiceQuestionAnswer.class)
})
public class AnswerUnit
...
But you would need to add "type" field to your client JSON.

Related

Different class types as RequestBody depending on RequestParam provided in a Spring Boot Controller?

So, I have a controller which takes in a request parameter and a body. The request body can be of various class types depending on the type of parameter. Currently I am using JsonNode for the body which works fine. Looks like this :
#PostMapping() public ResponseEntity<Response> save(#RequestParam("request type") RequestProcess process, #Valid #RequestBody JsonNode requestJson) {
I want to know whether it's possible to provide the body with the class type depending on the param provided. If yes how do I do it?
If this is not possible in REST, is there a chance I might be able to do this using GraphQl. I don't know much about GraphQL still researching.
TIA
The nearer you can reach is by using generics
class Controller < T > {
#PostMapping("/save")
ResponseEntity < Response > save(#RequestBody T requestJson) {}
}

how to apply post method to all request mappings in spring

How i can access to request POST data from different url-s to one controller method, for example I have /countries & /countries/{id} URL, It works very good with first one, because my code is
#RequestMapping(value = {"/{id}", "/{id}/"}, method = RequestMethod.GET)
public String getCountry(#PathVariable(value = "id", required = true) int id,ModelMap model) {
}
#RequestMapping(method = RequestMethod.POST)
public String deleteCountry(ModelMap model,HttpServletRequest request) {
}
And when I try to request POST data from second url I'm getting
HTTP Status 405 - Request method 'POST' not supported
Which is expectable because I haven't POST method with this mapping, but if I will be made one method for one mapping my code will be too ugly ant terrible, what solution I can use?
Hum why not add the "array" of value to your second method as well ?
#RequestMapping(value = {"", "/{id}"},method = RequestMethod.POST)
public String deleteCountry(ModelMap model,
HttpServletRequest request) {
Btw using POST verb to call an action that looks like it will delete the resource seems wrong, it should be a DELETE verb used
EDIT
But in reality, you should be creating 2 methods, as both those method are not supposed to do the same thing.
POST /countries should be creating country resources
POST /countries/{id} should be doing something else.
For an update prefer PUT /countries/{id}
And for a delete, prefer DELETE /countries/{id}
There is one way to have separate handler interceptors for different controllers.
Refer this link for details.
bind Spring HandlerInterceptor only to one controller
But I feel, it may be good you can create a common method to share business logic for this.
As Interceptor comes with proxy class for your controller which can be avoided unless you have complex logic.

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

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.

Handle HTML and non-HTML representations in controller with HttpMessageConverters

I have a Spring MVC controller for a RESTful resource (ServiceDirectoryController), which I want to serve both HTML (web page) and non HTML (web service) representations. I want to use view names and JSP to generate the HTML representation, but HttpMessageConverters for the other representations. How do I do that?
I've implemented the request handling method for the non-HTML representations. My internal (domain-model) representation is ThingNames, for which I have suitable message converters, so the request handling method is simply:
#RequestMapping(value = { "/directory/" }, method = RequestMethod.GET)
#ResponseBody
public ThingNames retrieveThings() {
...
}
Now I want to add the request handling method for the JSP page. So I need a method that indicates the view name and provides a suitable model. So, the obvious thing to do is this:
#RequestMapping(value = { "/directory/" }, method = RequestMethod.GET)
public String retrieveThings(Map< String, Object > model) {
...
}
with the view name as the return value.
But will that work? Is Spring clever enough to work out that the HTML representation should be handled by the second method, and use the first method for all other representations? And if not, how do I go about handling both JSP/HTML and non JSP/HTML representations?
According to a Spring blog post by Paul Chapman, my controller will work as written if I set up a PPA Content Negotiation Strategy. That is, if I configure Spring to consider clues about the wanted content type in the following descending order of preference:
Path extension (.htm, .tsv, etc.)
Request parameter (?format=htm, ?format=xls)
Accept header (proper HTTP content negotiation, which sadly works poorly with HTML browsers).
What is more, the Spring provided ContentNegotiationManagerFactoryBean operates in just that order.
In addition, you must annotate the handler methods in the controller so Spring knows that those methods should handle HTML content, using the consumes and produces values of the #RequestMapping annotation. That is, instead of simply writing
#RequestMapping(value = { "/directory/" }, method = RequestMethod.GET)
#ResponseBody
public String retrieveThings(Map< String, Object > model) {
...
}
write
#RequestMapping(value = { "/directory/" }, method = RequestMethod.GET,
produces = MediaType.TEXT_HTML_VALUE)
#ResponseBody
public String retrieveThings(Map< String, Object > model) {
...
}

Any way to accept bot json and regular objects in Spring controller

Is this possible to achive in any way?
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(#RequestBody Person personJson , Person personRegular){
Person person = personJson != null ? personJson : personRegular;
}
Yes, you can if you send an json representation of Person as POST body it will get bound to personJson, and the request parameters will get bound to personRegular, another way could be to send a id as a request parameter, populate a personRegular module attribute by performing a database lookup, and annotating your personRegular with #ModelAttribute

Resources