Let's say that I have a Spring 4 web application with a Hibernate-based persistence layer. I'd like to create a RestController
that supports basic CRUD operations for my models. Creating a method for fetching records works without a hitch:
@RequestMapping(value = "/stuff/list", method = RequestMethod.GET)
public List<Stuff> getStuff(){
return stuffService.findAll();
}
Jackson handles the object serialization, no problem. But what if I want to add a method for creating new records via a POST request? Is there any easy way to support a simple method like this?
@RequestMapping(value = "/stuff/new", method = RequestMethod.POST)
public Integer getStuff(@RequestParam("stuff") Stuff stuff){
return stuffService.save(stuff);
}
Is something like this possible? Or do I need to manually map posted form data to a new object?
SOLUTION
Here is how I solved my problem, there were a couple steps. First, my final controller method:
@RequestMapping(value = "/stuff/new", method = RequestMethod.POST)
public Integer getStuff(@RequestBody Stuff stuff){
return stuffService.save(stuff);
}
I already had a filter applied to all requests to the application API, to allow cross origin resource sharing, but modifications to this were needed to allow requests to specify content type:
public class SimpleCORSFilter implements Filter{
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override public void init(FilterConfig filterConfig) throws ServletException { }
@Override public void destroy() { }
}
Which is registered in my web.xml
file:
<filter>
<filter-name>cors</filter-name>
<filter-class>com.company.app.util.SimpleCORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/api/*</url-pattern>
</filter-mapping>
Now when I make a request, such as the one below, it will correctly map my submitted JSON to my model and persist a new instance:
var stuff = {
name: "Some stuff",
description: "This is some stuff."
}
$.ajax({
url: url,
method: "post",
dataType: "json",
data: JSON.stringify(stuff),
contentType: "application/json"
}).done(function(data){
console.log(data);
}).fail(function(x, status, e){
console.log(e);
});
Yes, absolutely. Jackson will automatically do a two-way mapping of entities, so if you want to
POST
something to the server just use the same JSON format that you see when you doGET
.To tell Spring that you want it to apply a deserializer to the content instead of attempt standard HTML form binding you use
@RequestBody
.@RequestParam
is telling it to look for an individual parameter with that name and apply standard databinding, not to deserialize the entire content of the POST into an object.