【问题标题】:How to post generic objects to a Spring controller?如何将通用对象发布到 Spring 控制器?
【发布时间】:2015-06-03 09:28:25
【问题描述】:

我想创建一个显示表单的网站。 表单的字段取决于请求参数(以及表单支持 bean)。 这是我呈现不同形式的控制器:

@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";
    }

}

对应的视图如下所示:

<!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>

以下是显示表单字段的示例片段:

<!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>

查找到的 bean 会是这样的:

public class MyExampleBean {

    private String firstName;

    private String lastName;

    // Getters & setters

}

表单已正确呈现,但我如何才能在控制器中接收回表单? 以及如何验证提交的 bean?我尝试了以下方法,但显然 它不能工作:

@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 创建了一个Object 的新实例,但提交的值丢失了。 那么我该如何完成这个任务呢?

【问题讨论】:

  • 看起来是范围问题,是否有参数告诉您想要该表单的会话范围?
  • 我没有在表单上设置范围。

标签: java spring spring-mvc


【解决方案1】:

如果您只想处理有限数量的 bean,您可以为每个 bean 设置一个 @RequestMapping 方法,所有这些都委托给一个私有方法完成这项工作。你可以找到一个例子here

如果您希望能够动态接受 bean,您将不得不手动完成 Spring 自动执行的操作:

  • 只使用请求而不是模型属性
  • 通过 PathVariable 名称在注册表中查找 bean
  • 明确地进行绑定

但希望 Spring 提供 WebDataBinder 的子类作为助手:

@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;
}

【讨论】:

  • 谢谢。第二种方法效果很好,但是如何添加验证?我使用binder.addValidators() 为我的对象添加了一个验证器。验证器使用ValidationUtils 检查上例中的firstNamelastName 是否为空。当我提交一个空表单并检查BindingResult 时,它说没有错误。
  • 刚看到绑定后要调用binder.validate()
【解决方案2】:

至于验证我不知道,但是要检索值,您可以尝试以下方法

(警告:我没有尝试此代码,因为在撰写本文时我无法尝试)。

processForm 处理程序更改为处理Object 类型的HttpServletRequest insead,并从参数映射中获取汇总值,例如:

@RequestMapping(value="/add/{name}", method=RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String processForm(@PathVariable String name, HttpServletRequest request) {
    Map<String, String[]> parameterMap = request.getParameterMap();
    ...
}

【讨论】:

    【解决方案3】:

    你应该对你的代码做两处修改,

    1. 使用 @SessionAttributes("bean") 注释您的 TestController 它将使您的模型属性与名称 bean 存储在会话中

    2. @ModelAttribute("bean") 添加到您的processForm 方法中,所以processForm(@PathVariable String name, @Valid @ModelAttribute("bean") Object bean)。由于模型属性bean 也注册为会话属性,因此它将从会话中检索它,并保留其值

    如果您的 processForm 方法位于不同的控制器中,请确保也使用 @SessionAttributes("bean") 注释该控制器

    以下应该是工作控制器的示例

    @SessionAttributes("bean")
    @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";
        }
    
        @RequestMapping(value = "/add/{name}", method = RequestMethod.POST)
        public String processForm(@PathVariable String name, @Valid @ModelAttribute("bean") Object bean) {
           System.out.println(bean);
           return "redirect:/add/" + name;
       }
    }
    

    【讨论】:

    • 违背了构建 RESTful 应用程序的目的
    猜你喜欢
    • 2011-04-28
    • 1970-01-01
    • 1970-01-01
    • 2011-08-19
    • 2019-05-20
    • 2014-11-26
    • 2018-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多