I have a rest controller:
#RestController
public abstract class CrudController {
#RequestMapping(
path = "delete",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON,
consumes = MediaType.APPLICATION_JSON)
public ResponseEntity<ResponseDTO<Void>> delete(
#RequestBody IdDTO request,
#RequestHeader("X-auth-token") String token,
BindingResult bindingResult
) {
//delete logic
}
#RequestMapping(
path = "read",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON,
consumes = MediaType.APPLICATION_JSON)
public ResponseEntity<ResponseDTO<DTO>> read(
#RequestBody IdDTO<User> request,
#RequestHeader("X-auth-token") String token,
BindingResult bindingResult
) {
//read logic
}
}
I wanted to add Spring Validation. It is obvious that for read method and for delete method should be different validators. I want to know it it possible to make two #InitBinder one for read and another for delete?
I think you're not quite familiar with usage of BindingResult. It is used in process of binding object from request to Java object.
It searches the class validator, or uses validation annotations used on class' fields. Object you want to bind needs to be annotated with #Valid.
For example:
#PostMapping
public ResponseEntity<Note> addNote(#Valid Note note, BindingResult result) {
if(result.hasErrors())
return ResponseEntity.badRequest().build();
noteService.save(note);
return ResponseEntity.ok(noteService.getNoteById(note.getId()));
}
What you're trying to do is validate something, but not quite bind it.
I also suggest using different http methods for delete and read(GET) in the same path rather than POST method in different paths.
Related
Can I create multiple HTTP POST methods with same request mapping with different requestbody
#RequestMapping("api/v1/")
#RequestMapping(value = "test" ,method = RequestMethod.POST)
public RObjet create(#RequestBody RBody rbody)
{
// do some process...
}
#RequestMapping("api/v1/")
#RequestMapping(value = "test" ,method = RequestMethod.POST)
public RObjet2 create2(#RequestBody RBody2 rbody)
{
// do something.
}
Is this possible.? How do we handle this in spring boot.
Yes, you can use POST Http Method for the same end point URI with different request body and also you could get different responses. One way to achieve this, is mapping requests using end point URI + Headers
e.g.
#RestController
#RequestMapping("/api/bills")
public class BillingController {
#RequestMapping(method = RequestMethod.POST, headers = "action=add-bill")
public BillId addBill(#Valid #RequestBody BillingData data) {
//Some code
}
#RequestMapping(method = RequestMethod.POST, headers = "action=delete-bill-by-id")
#ResponseStatus(code = HttpStatus.NO_CONTENT)
public void removeBill(#Valid #RequestBody BillId identifier) {
//Some code here to remove bill
}
}
In this case, both class methods in BillingController are mapped to the same HTTP Method (POST) and URI (/api/bills). The header action drives what class method in BillingController is going to be invoked once you point your post request to /api/bills
How to hit BillingController.addBill?
NOTE: I know that good REST API design dictates that if I want to delete records I should use DELETE method, however this sample was created only as reference to show how to use same URI/Method to handle 2 different end points.
You have to option for this.
it is possible with consumes field. You can use different consuming types.
You can user params field if you have in url.
#RequestMapping(value="/path", params="id")
public String test1(#RequestBody RBody body) {}
#RequestMapping(value="/path", params="name")
public String test2(#RequestBody RBody body) {}
Can someone explain the #RequestBody and #ResponseBody annotations in Spring 3? What are they for? Any examples would be great.
There is a whole Section in the docs called 16.3.3.4 Mapping the request body with the #RequestBody annotation. And one called 16.3.3.5 Mapping the response body with the #ResponseBody annotation. I suggest you consult those sections. Also relevant: #RequestBody javadocs, #ResponseBody javadocs
Usage examples would be something like this:
Using a JavaScript-library like JQuery, you would post a JSON-Object like this:
{ "firstName" : "Elmer", "lastName" : "Fudd" }
Your controller method would look like this:
// controller
#ResponseBody #RequestMapping("/description")
public Description getDescription(#RequestBody UserStats stats){
return new Description(stats.getFirstName() + " " + stats.getLastname() + " hates wacky wabbits");
}
// domain / value objects
public class UserStats{
private String firstName;
private String lastName;
// + getters, setters
}
public class Description{
private String description;
// + getters, setters, constructor
}
Now if you have Jackson on your classpath (and have an <mvc:annotation-driven> setup), Spring would convert the incoming JSON to a UserStats object from the post body (because you added the #RequestBody annotation) and it would serialize the returned object to JSON (because you added the #ResponseBody annotation). So the Browser / Client would see this JSON result:
{ "description" : "Elmer Fudd hates wacky wabbits" }
See this previous answer of mine for a complete working example: https://stackoverflow.com/a/5908632/342852
Note: RequestBody / ResponseBody is of course not limited to JSON, both can handle multiple formats, including plain text and XML, but JSON is probably the most used format.
Update
Ever since Spring 4.x, you usually won't use #ResponseBody on method level, but rather #RestController on class level, with the same effect.
Here is a quote from the official Spring MVC documentation:
#RestController is a composed annotation that is itself meta-annotated
with #Controller and #ResponseBody to indicate a controller whose
every method inherits the type-level #ResponseBody annotation and,
therefore, writes directly to the response body versus view resolution
and rendering with an HTML template.
#RequestBody : Annotation indicating a method parameter should be bound to the body of the HTTP request.
For example:
#RequestMapping(path = "/something", method = RequestMethod.PUT)
public void handle(#RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}
#ResponseBody annotation can be put on a method and indicates that the return type should be written straight to the HTTP response body (and not placed in a Model, or interpreted as a view name).
For example:
#RequestMapping(path = "/something", method = RequestMethod.PUT)
public #ResponseBody String helloWorld() {
return "Hello World";
}
Alternatively, we can use #RestController annotation in place of #Controller annotation. This will remove the need to using #ResponseBody.
for more details
Below is an example of a method in a Java controller.
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public HttpStatus something(#RequestBody MyModel myModel)
{
return HttpStatus.OK;
}
By using #RequestBody annotation you will get your values mapped with the model you created in your system for handling any specific call. While by using #ResponseBody you can send anything back to the place from where the request was generated. Both things will be mapped easily without writing any custom parser etc.
#RestController is a composed annotation that is itself meta-annotated with #Controller and #ResponseBody to indicate a controller whose every method inherits the type-level #ResponseBody annotation and, therefore, writes directly to the response body versus view resolution and rendering with an HTML template
So instead of marking your class as #Controller use #RestController instead and remove the #requestbody annotation from you class
heres an example:
#RestController
public class MomController {
#RequestMapping("/sugar") // maped to the url /sugar
public String addSugar() {
return "here is your sugar";
}
}
package com.programmingfree.springshop.controller;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.programmingfree.springshop.dao.UserShop;
import com.programmingfree.springshop.domain.User;
#RestController
#RequestMapping("/shop/user")
public class SpringShopController {
UserShop userShop=new UserShop();
#RequestMapping(value = "/{id}", method = RequestMethod.GET,headers="Accept=application/json")
public User getUser(#PathVariable int id) {
User user=userShop.getUserById(id);
return user;
}
#RequestMapping(method = RequestMethod.GET,headers="Accept=application/json")
public List<User> getAllUsers() {
List<User> users=userShop.getAllUsers();
return users;
}
}
In the above example they going to display all user and particular id details now I want to use both id and name,
1) localhost:8093/plejson/shop/user <---this link will display all user details
2) localhost:8093/plejson/shop/user/11 <----if i use 11 in link means, it will display particular user 11 details
now I want to use both id and name
localhost:8093/plejson/shop/user/11/raju <-----------------like this
it means we can use any one in this please help me out.....
I am new to Spring and Rest Endpoints.
I have a controller, which accepts #RequestParam and returns a JSON Response.
By default the #RequestParam required = "true", which is how I need it.
I am using Spring 3.1.3
This is my Get Method in the controller:
#Controller
#RequestMapping("/path")
public class MyController{
#RequestMapping(value = "/search/again.do", method = RequestMethod.GET, produces = {
"application/json"
})
public ResponseEntity<?> find(#RequestParam(value = "test", required = true) final String test) {
return new ResponseEntity<String>("Success ", HttpStatus.OK);
}
}
When I send a get with the request param it hits the endpoint , which is how I expect.
Example : path/search/again.do?test=yes
Everything is perfect.
This is where I am having issue:
When I send a Get with that value missing:
Example: path/search/again.do
I get a 400 Bad Request. May be this is correct.
But what I want to achieve is. When the required value is missing in the GET request.
I can send a JSON response as that #RequestParam Value test is missing.
Can anybody guide me how to achieve this.
I am not sure what I am missing.
Thanks in advance.
If you look closely at your code, you'll see that the answer is staring right at you. Just change required to false and you should be good to go. When the user doesn't provide a value for GET parameter test, then you can return a special message.
#Controller
#RequestMapping("/path")
public class MyController {
#RequestMapping(value = "/search/again.do", method = RequestMethod.GET, produces = {
"application/json"
})
public ResponseEntity<?> find(#RequestParam(value = "test", required = false) final String test) {
if (test == null) {
return new ResponseEntity<String>("test parameter is missing", HttpStatus.OK);
}
else {
return new ResponseEntity<String>("Success ", HttpStatus.OK);
}
}
}
Solution 1: You can use custom #ExceptionHandler in your controller, e.g
#ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseEntity<?> paramterValidationHandler(HttpServletResquest request){
//validate the request here and return an ResponseEntity Object
}
Solution 2: Would be custom spring ErrorController which I never have tried myself but it possible to override it.
Solution 3: You can write an ControllerAdvice for a global controller exception handling.
Well if you set the parameter test is required. U just can't send the request without that param. Try to change the param required= false and handle the missing param in the method. You can us something likeif(test==null) throw new Exception("Param test missing")
I would like to know how to read a flash attributes after redirection in Spring MVC 3.1.
I have the following code:
#Controller
#RequestMapping("/foo")
public class FooController {
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(...) {
// I want to see my flash attributes here!
}
#RequestMapping(value = "/bar", method = RequestMethod.POST)
public ModelAndView handlePost(RedirectAttributes redirectAttrs) {
redirectAttrs.addFlashAttributes("some", "thing");
return new ModelAndView().setViewName("redirect:/foo/bar");
}
}
What I am missing?
Use Model, it should have flash attributes prepopulated:
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(Model model) {
String some = (String) model.asMap().get("some");
// do the job
}
or, alternatively, you can use RequestContextUtils#getInputFlashMap:
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(HttpServletRequest request) {
Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);
if (inputFlashMap != null) {
String some = (String) inputFlashMap.get("some");
// do the job
}
}
P.S. You can do return return new ModelAndView("redirect:/foo/bar"); in handlePost.
EDIT:
JavaDoc says:
A RedirectAttributes model is empty when the method is called and is
never used unless the method returns a redirect view name or a
RedirectView.
It doesn't mention ModelAndView, so maybe change handlePost to return "redirect:/foo/bar" string or RedirectView:
#RequestMapping(value = "/bar", method = RequestMethod.POST)
public RedirectView handlePost(RedirectAttributes redirectAttrs) {
redirectAttrs.addFlashAttributes("some", "thing");
return new RedirectView("/foo/bar", true);
}
I use RedirectAttributes in my code with RedirectView and model.asMap() method and it works OK.
Try this:
#Controller
public class FooController
{
#RequestMapping(value = "/foo")
public String handleFoo(RedirectAttributes redirectAttrs)
{
redirectAttrs.addFlashAttribute("some", "thing");
return "redirect:/bar";
}
#RequestMapping(value = "/bar")
public void handleBar(#ModelAttribute("some") String some)
{
System.out.println("some=" + some);
}
}
works in Spring MVC 3.2.2
For all those like me who were having problems with seeing the POST url in the browser when a validation would fail.
The POST url is a private url that should not be exposed to users but it was automatically rendered when a validation failed. i.e. if a field was below a minimum length. I was using #Valid. I wanted the original GET url of the form to show at all times even when validation bounced back to the form, so I did the following:
if (validation.hasErrors()) {
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.story", validation);
redirectAttributes.addFlashAttribute("story", story);
return new ModelAndView("redirect:/january/2015");
where story is the form object representation, redirectAttributes are RedirectAttributes you put in the method signature and validation is the BindingResult. /january/2015 is the mapping to the GET controller where the form lives.
After this implementation, in the mapping for /january/2015, story comes in intact as follows:
Story story= (Story) model.asMap().get("story");
//story from the POST method
I had to augment my GET method and check if this was not null. If not null, then send this to the form else I would send a newly initialized Story type to the form as default behaviour before.
In this manner, I am able to return to the form with the bindingresults intact (errors show on form) but have my GET url in place of the post url.
Can someone explain the #RequestBody and #ResponseBody annotations in Spring 3? What are they for? Any examples would be great.
There is a whole Section in the docs called 16.3.3.4 Mapping the request body with the #RequestBody annotation. And one called 16.3.3.5 Mapping the response body with the #ResponseBody annotation. I suggest you consult those sections. Also relevant: #RequestBody javadocs, #ResponseBody javadocs
Usage examples would be something like this:
Using a JavaScript-library like JQuery, you would post a JSON-Object like this:
{ "firstName" : "Elmer", "lastName" : "Fudd" }
Your controller method would look like this:
// controller
#ResponseBody #RequestMapping("/description")
public Description getDescription(#RequestBody UserStats stats){
return new Description(stats.getFirstName() + " " + stats.getLastname() + " hates wacky wabbits");
}
// domain / value objects
public class UserStats{
private String firstName;
private String lastName;
// + getters, setters
}
public class Description{
private String description;
// + getters, setters, constructor
}
Now if you have Jackson on your classpath (and have an <mvc:annotation-driven> setup), Spring would convert the incoming JSON to a UserStats object from the post body (because you added the #RequestBody annotation) and it would serialize the returned object to JSON (because you added the #ResponseBody annotation). So the Browser / Client would see this JSON result:
{ "description" : "Elmer Fudd hates wacky wabbits" }
See this previous answer of mine for a complete working example: https://stackoverflow.com/a/5908632/342852
Note: RequestBody / ResponseBody is of course not limited to JSON, both can handle multiple formats, including plain text and XML, but JSON is probably the most used format.
Update
Ever since Spring 4.x, you usually won't use #ResponseBody on method level, but rather #RestController on class level, with the same effect.
Here is a quote from the official Spring MVC documentation:
#RestController is a composed annotation that is itself meta-annotated
with #Controller and #ResponseBody to indicate a controller whose
every method inherits the type-level #ResponseBody annotation and,
therefore, writes directly to the response body versus view resolution
and rendering with an HTML template.
#RequestBody : Annotation indicating a method parameter should be bound to the body of the HTTP request.
For example:
#RequestMapping(path = "/something", method = RequestMethod.PUT)
public void handle(#RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}
#ResponseBody annotation can be put on a method and indicates that the return type should be written straight to the HTTP response body (and not placed in a Model, or interpreted as a view name).
For example:
#RequestMapping(path = "/something", method = RequestMethod.PUT)
public #ResponseBody String helloWorld() {
return "Hello World";
}
Alternatively, we can use #RestController annotation in place of #Controller annotation. This will remove the need to using #ResponseBody.
for more details
Below is an example of a method in a Java controller.
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public HttpStatus something(#RequestBody MyModel myModel)
{
return HttpStatus.OK;
}
By using #RequestBody annotation you will get your values mapped with the model you created in your system for handling any specific call. While by using #ResponseBody you can send anything back to the place from where the request was generated. Both things will be mapped easily without writing any custom parser etc.
#RestController is a composed annotation that is itself meta-annotated with #Controller and #ResponseBody to indicate a controller whose every method inherits the type-level #ResponseBody annotation and, therefore, writes directly to the response body versus view resolution and rendering with an HTML template
So instead of marking your class as #Controller use #RestController instead and remove the #requestbody annotation from you class
heres an example:
#RestController
public class MomController {
#RequestMapping("/sugar") // maped to the url /sugar
public String addSugar() {
return "here is your sugar";
}
}
package com.programmingfree.springshop.controller;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.programmingfree.springshop.dao.UserShop;
import com.programmingfree.springshop.domain.User;
#RestController
#RequestMapping("/shop/user")
public class SpringShopController {
UserShop userShop=new UserShop();
#RequestMapping(value = "/{id}", method = RequestMethod.GET,headers="Accept=application/json")
public User getUser(#PathVariable int id) {
User user=userShop.getUserById(id);
return user;
}
#RequestMapping(method = RequestMethod.GET,headers="Accept=application/json")
public List<User> getAllUsers() {
List<User> users=userShop.getAllUsers();
return users;
}
}
In the above example they going to display all user and particular id details now I want to use both id and name,
1) localhost:8093/plejson/shop/user <---this link will display all user details
2) localhost:8093/plejson/shop/user/11 <----if i use 11 in link means, it will display particular user 11 details
now I want to use both id and name
localhost:8093/plejson/shop/user/11/raju <-----------------like this
it means we can use any one in this please help me out.....