I want a controller with the following mapping (incomplete):
#GetMapping(/searchitems)
public #ResponseBody Page<Item> get(Item probe)
From the Item probe parameter I want to query by example in a repository of items and return the result.
Question:
How can I complete the mapping above for a search URL? As search URL I was thinking something like /searchitems?itemAttributeA=foo&itemAttributeB=bar&...itemAttributeZ=xyz. How can I tell spring to inject the passed request parameters into the Item probe fields with the same names?
Adding #ModelAttribute should bind the individual request parameters into your Item POJO.
public #ResponseBody Page<Item> get(#ModelAttribute Item probe)
You can create a POJO and pass as a parameter in the controller class. Pojo should have the fields which you want to read and set. Spring will read and map those attributes in the Pojo which you will define as the request.
#GetMapping(/searchitems)
public ResponseEntity<List<Items>> searchItems(ItemRequest itemRequest) {
}
Only thing needs to be taken care is to check for binding result. If there are errors, we need to stop the request and handle or throw.
For e.g. All of the below attributes in the URL will be set in the Pojo.
https://domain/search-items?pageNumber=1&sortOrder=ascending&itemName=test&itemType=apparel&sortField=itemId&pageSize=5
You can use #RequestParam for this.
public #ResponseBody Page<Item> get(#RequestParam("itemAttributeA") String itemAttributeA ,
#RequestParam("itemAttributeB") String itemAttributeB,...)
Related
In a SpringBoot REST application I have a TableRequest type that contains column sorting, filtering, and paging details for GET requests for tabular data. It's generic in that it doesn't care what the specific data being requested is, it only specifies generic table parameters. As such it's applicable across many different controller methods. Also, because it applies to GET requests the fields are passed as request parameters (no #RequestBody json parameter). I've got a #ModelAttribute method inside the controller class that parses the request parameters into a TableRequest object, then the actual #RequestMapping method receives that object as a #ModelAttribute parameter.
Because the TableRequest class is generic, I'd like to be able to use it across multiple controllers without having to copy the parsing logic into every one. I'm wondering if there's a Spring-y annotation-based way of reusing the same #ModelAttribute method any time a controller has a TableRequest input parameter.
Thanks in advance :)
My Solution (based on selected answer below)
I created a #TableRequestController annotation and a corresponding #ControllerAdvice class that applies only to controller classes that have that annotation. That ControllerAdvice class includes the #ModelAttribute method tht parses the GET request parameters into a TableRequest object.
The one important caveat here is that the new #TableRequestController may only be applied to Controller class as a whole, not to individual controller methods. As such, I created a separate inner controller class, tagged with that annotation, whose #RequestMapping methods all accept a TableRequest object.
#TableRequestController:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface TableRequestController {}
ControllerAdvice class:
#ControllerAdvice(annotations = TableRequestController.class)
public class TableRequestControllerAdvice {
#ModelAttribute
public TableRequest tableRequest(
#RequestParam Map<String, String> params,
#RequestParam int pageStart,
#RequestParam int pageSize) {
return new TableRequest(params, pageStart, pageSize);
}
}
TableRequest REST controller class:
#RestController
#TableRequestController
public static class MyTableRequestController {
#RequestMapping("/the/table/request/url")
public MyResponse makeTableRequest(
TableRequest tableRequest) {
return new MyResponse(tableRequest);
}
}
You can use #ControllerAdvice. Everything defined here applies to all controllers, or a defined subset, if you prefer that.
Documentation
Another alternative (better imho) is to write a message converter. It only handles one specific type. You no longer need #ModelAttribute but simply have a TableRequest parameter in you controller method.
In a SpringBoot REST application I have a TableRequest type that
contains column sorting, filtering, and paging details for GET
requests for tabular data. It's generic in that it doesn't care what the specific data being requested is, it only specifies generic table parameters
This means what you have is a utility service.
So define the class as service and access it inside the views.
Access any beans in your application context using SpringEL’s syntax: ${#myBean.doSomething()}
I have a scenario to pass values from one request to another subsequent rquest. (i.e) I will call 'Controller1' on the first request and take the request parameters or query string and should send them to 'Controller2' as 'new request'.
Strictly I should not use any of the following approaches.
should not use sessions.
should not use cookies.
should not use requestdispatcher.forward(--).
without FlashAttributes (which internally uses session, which won't work in 'Clustered environmnets').
should not expose the ModelAttribues in request parameters in case of redirection (i.e) I should not even expose them as request parameters using spring RedirectView.
please let me know, if we have any alternative approch.
Thanks in advance.
You could call the underlying method directly
So if you have as controller2 :
#RequestMapping(value = "/MyURL", method = RequestMethod.POST)
public String myMethod(final BaseDTO baseDTO, Model model) {}
Inject controller2 into controller1 and call "normally":
controller2.myMethod(baseDTO, model);
I am working on spring REST APIs. In requirements, there are 2 POST requests with same URL but different request body. Since Spring MVC must have unique mappings across controller, I have to pre-process the request body to map to a specific POJO.
On the basis of session_type in request body, I have to map the request to specific POJO (JSON -> JAVA POJO).
For example, if 'session_type' in request body is 'typeX' then the request should map to ClassX POJO. If 'session_type' in request body is 'typeY' then the request should map to ClassY POJO.
If there a way to do it using some kind of requestbody annotation?
If you want to bind typeX and typeY, then you definitely need 2 handlers. But, why wouldn't we use param option of #RequestMapping:
#RequestMapping(method = RequestMethod.POST,
value = "/url", params = "session_type=typeX")
public String handleTypeX(#RequestBody #ModelAttribute TypeX typeX){
//TODO implement
}
#RequestMapping(method = RequestMethod.POST,
value = "/url", params = "session_type=typeY")
public String handleTypeY(#RequestBody #ModelAttribute TypeY typeY){
//TODO implement
}
If you need some preparations (f.e. normalize params or perform model binding manually), then the approach above you may combine along with #InitBinder, but please note, that #InitBinder needs exact ULR's rules along with #ModelAttribute parameters in handlers.
EDIT: In Spring MVC there is no possibility to use 2 handlers for exact URL, i.e. when method/URL/params/consumes type are the same.
Thus I suggest use unified handler, where you would check necessary parameter and then manually convert into corresponding class. For finding necessary class I suppose it would be better to use Strategy pattern:
//class resolver according "session_type" parameter
//note, that you can use Spring autowiring capabilities
private final Map<String, Class> TYPES_CONTEXT = new HashMap<String, Class>(){
{
this.put("x", TypeX.class);
this.put("y", TypeY.class);
//TODO probably other classes
}
}
#RequestMapping(method = RequestMethod.POST,
value = "/url")
public #ResponseBody String handleAnyType(#RequestBody Map<String, String> body){
String sessionType = body.get("session_type");
//TODO handle case if sessionType is NULL
Class convertedClass = TYPES_CONTEXT.get(sessionType);
//TODO handle case if class is not found
Object actualObject = objectMapper.convertValue(body, convertedClass);
//now we use reflection for actual handlers, but you may refactor this in the way you want, f.e. again with Strategy pattern
//note that current approach there should be contract for methods names
Method actualHandler = this.getClass().getMethod("handle" + actualObject.getClass().getSimpleName());
return (String)actualHandler.invoke(this, actualObject);
}
public String handleTypeX(TypeX typeX){
//TODO implement
}
public String handleTypeY(TypeY typeY){
//TODO implement
}
//TODO probably other methods
This approach doesn't handle validation and some things were omitted, but I believe this might be helpful.
I think you should created controller with one method for both types, and call required component\method in it depending on typeX or typeY.
GETs shouldn't have request bodies, or at least if they do, the server side isn't required to do anything with them. As you describe it, this API isn't RESTful.
Assuming you don't care about that, try creating a controller method that takes a parent class of TypeX and TypeY, or interface that both TypeX and TypeY implement, annotate it with #SomethingMeaningfulToYou, then use a web argument method resolver to instantiate the child class you want.
It's a hack around a broken API though.
there are 2 POST requests with same URL but different request body
For a RESTful interface, the same URL should always indicate the same resource. The body of a request may contain different representations of that resource. You could create different HttpMessageContverter classes for the two different kinds of representation.
My request is ajax based and am calling to one of spring multiaction controller method,I able to create json file using Gson library. Is any way to return json view from controller method.
You can just let your method return the JSON String if you use the #ResponseBody annotation and use one of the methods listed in the answers to my previous question:
In Spring MVC, how can I set the mime
type header when using
#ResponseBody
I'm a total newbie but I've read some where that when you dont specify any logical view mapping for a controller and you return a model map from a handler method (#Controller, #RequestMapping) it should transform the model object to json and return it.
I'm quite sure this has to be in the docs somewhere, but I've looked for days and haven't spotted it. I'm probably staring myself blind when it's right in front of me, so sorry for asking an abvious question, but....
#RequestMapping(method = RequestMethod.POST)
public ModelAndView post(#ModelAttribute("user") User user) {
ModelAndView mav = new ModelAndView(jsonView);
//....
return mav;
}
This is my POST function as part of my controller, and I'd like to try it out. So I fire up Poster, the Firefox REST tester that I use for trying out my functions, and fire a POST to http://localhost:8000/userController with parameters { firstname = "foo", lastname = "bar }. That gives me:
org.springframework.web.HttpSessionRequiredException: Session attribute 'user' required - not found in session
So I try with { user.firstname = "foo", user.lastname = "bar" }, same error. What parameters do I need to send in a POST or PUT request in order to use this mechanism that automatically maps my parameters to an object?
Cheers
Nik
That effect apparnetly occurs if you have #SessionAttributes annotation at your class. This causes Spring to expect the model attribute to be stored in the session from a prior GET web request, that put the object into the model.
This behaviour allows partial binding of request parameters to model objects. The workflow is as follows:
you receive a GET request to populate the model
Spring binds all model objects you configured either by name or type in #SessionAttributes to the session
you receive a POST request with updated data
Spring takes the objects from the session and binds the request data to them
Othwerise Spring would populate an empty new object which is mostly not the desired behaviour.
That annotation will search in the session an attribute named 'user', while the parameters you send in the POST are stored as parameters of the request:
//Get a session attribute:
request.getSession().getAttribute("user");
//Get a request parameter:
request.getParameter("firstname");
More here
No annotation should be necessary on the User method parameter, Spring will recognise it as a command object and bind to it without the annotation.
#ModelAttribute is slightly confusing, I find, its meaning seems to vary from situation to situation.