nfvindaloo
nfvindaloo

Reputation: 938

Spring MVC 3.1 RESTful Controller

Im about to write a Spring MVC Controller to serve/receive HTML forms and JSON. The best way seems to be to use a RESTful controller, but as its the first one Ive written I want to do it right!

Is it possible to have one method which will return either a view to be rendered by InternalResourceViewResolver if its an HTML request, or an Entity to be rendered as JSON if its an ajax request?

The same goes for updates, can you write a single controller method that will accept either an object thats been converted from incoming JSON or a @Valid object from an HTML form, depending on the content-type?

I seems to me you must be able to, otherwise why have support for DELETE and PUT in HTML forms using the sf taglib form element? Just can't seem to find an explanation of how to do it anywhere!

Cheers! NFV

Upvotes: 2

Views: 1309

Answers (2)

sbzoom
sbzoom

Reputation: 3373

I'll give this a go.

Here is what I have in my Configuration class:

@Bean(name = "viewResolver")
public ContentNegotiatingViewResolver viewResolver() {
    final ContentNegotiatingViewResolver contentNegotiatingViewResolver = new ContentNegotiatingViewResolver();
    contentNegotiatingViewResolver.setOrder(1);
    contentNegotiatingViewResolver.setFavorPathExtension(true);
    contentNegotiatingViewResolver.setFavorParameter(true);
    contentNegotiatingViewResolver.setIgnoreAcceptHeader(false);
    final Map<String, String> mediaTypes = new HashMap<String, String>();
    mediaTypes.put("json", "application/x-json");
    mediaTypes.put("json", "text/json");
    mediaTypes.put("json", "text/x-json");
    mediaTypes.put("json", "application/json");
    mediaTypes.put("xml", "text/xml");
    mediaTypes.put("xml", "application/xml");
    contentNegotiatingViewResolver.setMediaTypes(mediaTypes);
    final List<View> defaultViews = new ArrayList<View>();
    defaultViews.add(jsonView());
    defaultViews.add(xmlView());
    contentNegotiatingViewResolver.setDefaultViews(defaultViews);
    return contentNegotiatingViewResolver;
}

@Bean(name = "xStreamMarshaller")
public XStreamMarshaller xStreamMarshaller() {
    return new XStreamMarshaller();
}

@Bean(name = "xmlView")
public MarshallingView xmlView() {
    final MarshallingView marshallingView = new MarshallingView(xStreamMarshaller());
    marshallingView.setContentType("application/xml");
    return marshallingView;
}

@Bean(name = "jsonView")
public MappingJacksonJsonView jsonView() {
    return new MappingJacksonJsonView();
}

And here is what goes in the Controller.

@RequestMapping(value = { "/pets" }, method = RequestMethod.GET)
public String list(Model model) {
    model.addAttribute("pets", petRepository.findAll());
    return "pets/list";
}

@RequestMapping(value = { "/pets" }, method = RequestMethod.POST)
public String create(@Valid @RequestBody Pet pet, Model model) {
    petRepository.save(pet);
    return "redirect:pets/read/" + pet.getId();
}

@RequestMapping(value = { "/pets/{petId}" }, method = RequestMethod.GET)
public String read(@PathVariable Integer petId, Model model) {
    model.addAttribute("pet", petRepository.findOne(petId));
    return "pets/read";
}

@RequestMapping(value = { "/pets" }, method = RequestMethod.PUT)
public String update(@Valid @RequestBody Pet pet, Model model) {
    petRepository.save(pet);
    return "redirect:pets/read/" + pet.getId();
}

@RequestMapping(value = { "/pets/{orderId}" }, method = RequestMethod.DELETE)
public void delete(@PathVariable Integer petId, Model model) {
    petRepository.delete(petId);
}

From my experience, you can submit an HTML form or a JSON object as a @RequestBody. Give it a try.

Upvotes: 2

Cristiano Fontes
Cristiano Fontes

Reputation: 5088

It's surely possible but I don't see why that would be useful.

In my opnion a controller method should be created for every action you will need, making a controller handle 2 different kinds of input will make this controller method complicated to read and maintain over time.

A way to do it is using consumes in @RequestMapping annotation like this, then you write 2 methods and each watches for it's kind of input.

@RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json");

source of this code

Upvotes: 0

Related Questions