Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to post generic objects to a Spring controller?

I want to create a website that displays a form. The fields of the form depend on a request parameter (and also the form backing bean). This is my controller that renders the different forms:

@Controller
public class TestController {

    @Autowired
    private MyBeanRegistry registry;

    @RequestMapping("/add/{name}")
    public String showForm(@PathVariable String name, Model model) {
        model.addAttribute("name", name);
        model.addAttribute("bean", registry.lookup(name));

        return "add";
    }

}

The corresponding view looks like this:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
    <form method="post" th:action="@{|/add/${name}|}" th:object="${bean}">
        <th:block th:replace="|${name}::fields|"></th:block>
        <button type="submit">Submit</button>
    </form>
</body>
</html>

Following is an example fragment that displays the form fields:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
    <th:block th:fragment="fields">
        <label for="firstName">First name</label><br />
        <input type="text" id="firstName" th:field="*{firstName}" /><br />
        <label for="lastName">Last name</label><br />
        <input type="text" id="lastName" th:field="*{lastName}" />
    </th:block>
</body>
</html>

The looked up bean would be like this:

public class MyExampleBean {

    private String firstName;

    private String lastName;

    // Getters & setters

}

The form is rendered correctly, but how can I receive the form back in the controller? And how can I validate the submitted bean? I tried the following method, but obviously it can not work:

@RequestMapping(value = "/add/{name}", method = RequestMethod.POST)
public String processForm(@PathVariable String name, @Valid Object bean) {
    System.out.println(bean);

    return "redirect:/add/" + name;
}

Spring creates a new instance of Object but the submitted values are lost. So how can I accomplish this task?

like image 203
stevecross Avatar asked Jun 03 '15 09:06

stevecross


People also ask

Is controller thread safe in spring?

When such a request arrives, the container picks a Thread from the pool and, within that Thread, executes the service() method on the DispatcherServlet which dispatches to the correct @Controller instance that Spring registered for you (from your configuration). So YES, Spring MVC classes must be thread safe.

Is @controller a spring boot annotation?

The @Controller annotation indicates that a particular class serves the role of a controller. Spring Controller annotation is typically used in combination with annotated handler methods based on the @RequestMapping annotation. It can be applied to classes only. It's used to mark a class as a web request handler.

In which layer @controller is used in spring?

In Spring MVC, controller methods are the final destination point that a web request can reach. After being invoked, the controller method starts to process the web request by interacting with the service layer to complete the work that needs to be done.

How do I add a spring boot controller?

Creating the controller class web package in the src/main/java source folder and selecting New → Class. (If Class is not offered on the New menu, then the Java perspective may not be being used. Look for the Class option under Other… in the Java section.) Name the new class GreenPagesController and press Finish.


1 Answers

If you only wanted to deal with a limited number of beans, you could have one @RequestMapping method for each bean, all delegating to a private method that would do the job. You can find an example here.

If you want to be able to accept bean dynamically, you will have to do by hand what Spring does automagically :

  • only use the request and not a model attribute
  • find the bean in registry by the PathVariable name
  • do explicitely the binding

But hopefully Spring offers the subclasses of WebDataBinder as helpers :

@RequestMapping(value = "/add/{name}", method = RequestMethod.POST)
public String processForm(@PathVariable String name, WebRequest request) {
    //System.out.println(bean);

    Object myBean = registry.lookup(name);
    WebRequestDataBinder binder = new WebRequestDataBinder(myBean);
    // optionnaly configure the binder
    ...
    // trigger actual binding of request parameters
    binder.bind(request);
    // optionally validate
    binder.validate();
    // process binding results
    BindingResult result = binder.getBindingResult();
    ...

    return "redirect:/add/" + name;
}
like image 152
Serge Ballesta Avatar answered Sep 30 '22 20:09

Serge Ballesta