0

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);
  });
2
  • Is the content an html form POST or JSON? Commented Aug 28, 2014 at 17:28
  • @Affe: JSON, sent either by a browser AJAX request or via a Spring RestTemplate. Commented Aug 28, 2014 at 17:30

2 Answers 2

1

To tell Spring that you want it to apply a deserializer to the content instead of attempt standard HTML form binding you use @RequestBody.

@RequestMapping(value = "/stuff/new", method = RequestMethod.POST)
public Integer getStuff(@RequestBody Stuff stuff){
    return stuffService.save(stuff);
}

@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.

Sign up to request clarification or add additional context in comments.

8 Comments

Do I need to explicitly set consumes = "application/something"? Or will it expect the proper JSON format? Are any additional request headers needed?
Only if you want to route requests with different headers to different places or lock it down. For simple things the out-of-the-box <mvc:annotation-driven /> will "get it right." (note if you specify consumes, requests with valid content but missing the header will be turned away, you may or may not want that.)
Writing my controller method like you have it results in 415 (Unsupported Media Type) errors. Another SO answer (stackoverflow.com/questions/11492325/…) suggests altering the request headers, but this did not help either.
Did you configure your dispatcher with mvc:annotation-driven or set it up manually? Is Jackson on the classpath?
I used <mvc:annotation-driven>, but with a modification (see above). Jackson is on the classpath and working fine for object serialization.
|
1

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 do GET.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.