【问题标题】:Spring 3 AJAX POST request with @RequestBody and @ModelAttribute and @SessionAttribute used together?Spring 3 AJAX POST 请求与@RequestBody 和@ModelAttribute 以及@SessionAttribute 一起使用?
【发布时间】:2013-05-10 01:19:23
【问题描述】:

有一个 Java Spring MVC Web 应用程序,并且正在发出一个 jquery ajax 发布请求。我的控制器设置为接收和发送 json 数据。一切正常,JSON 字符串格式正确,Controller 可以创建和填充一个 Command 对象,并使用 JSON 请求数据的内容填充它。但是,我正在更新联系人对象的数据,而我的 JSP 表单元素仅包含数据库更新所需的所有数据的子集。在我对带有表单的 JSP 页面的初始 GET 请求中,我从数据库中检索所有必要的数据,填充一个 Contact Command 对象,然后将该命令对象绑定到模型。

如果我正在执行普通的 POST 提交表单提交,我相信只需将我的命令对象声明为 @SessionAttribute,并在我的 onSubmit() POST 方法中使用 @ModelAttribute 引用该命令对象就足够了。 Spring 将从我的会话中检索已经填充的命令对象,然后绑定(覆盖)那些由于 POST 请求而更改的值。然后,这个更新的命令对象可以用作数据库更新的参数。

但是,我正在使用 Spring 3 并利用 @RequestBody 参数类型。我无法让 Spring 既给我会话对象又自动绑定请求中的新值。它要么只给我旧的会话命令对象(不应用更改),要么给我一个只包含来自 POST 请求的值的新命令对象。

这是一个小代码 - 不起作用:

@SessionAttributes("contactCommand")
@Controller
public class ContactController {


  @RequestMapping(value = "/editContact", method=RequestMethod.GET)
public String init(ModelMap model, Locale locale, HttpServletRequest request, HttpServletResponse response) throws GeneralException {
    final ContactCommand cmd = new ContactCommand();
    // populate with data from DB etc
    model.addAttribute("contactCommand", cmd);
    // etc
}

@RequestMapping(value="/editContact",method=RequestMethod.POST, consumes = "application/json", produces = "application/json")
public @ResponseBody Map<String, ? extends Object> editContactInfo(@RequestBody @ModelAttribute("contactCommand") ContactCommand cmd, HttpServletRequest request, HttpServletResponse response) throws GeneralException {

// do business logic with command object here

}

谁能告诉我将@RequestBody 与JSON 请求数据结合使用并绑定到现有的/@ModelAttribute 填充的Command 对象的“标准”或“最简单”的方式是什么,以便Command 对象完全由旧的和新数据(与使用完整的 POST http 提交很容易实现相同的方式)。

一个相关的问题是上面的代码有什么问题? @SessionAttribute 和带有 JSON 内容的 @RequestBody 可以一起使用吗?如果有,请说明方法!非常感谢您的任何意见。

我的解决方法是让 Spring 创建新的 Command 对象并自动填充表单数据。然后从会话中手动单独调用/检索旧命令对象,最后手动将表单提交中不存在的所有属性复制到新命令对象中。现在,我将所有必要的数据放在一个命令对象中,以应用我的 SQL 更新。一定有更简单的方法.... ;)

更新:

今天在进一步研究这个问题时发现了这个 SOF 帖子:

Spring Partial Update Object Data Binding

似乎没有现成的 SPRING 解决方案,但需要知道处理它的最佳方法。就我而言,是的,我使用的是嵌套域对象,因此帖子中提供的解决方法并不好。有没有人有任何其他想法?需要明确的是,我希望将 JSON 格式的数据 POST 到控制器(不仅仅是 http 表单发布数据)。

好的,我已经为此打开了一个 Spring Source JIRA 请求,也许这是一个非常需要的改进:

https://jira.springsource.org/browse/SPR-10552

否则,这是一个以巧妙的方式利用 Jackson 转换功能的案例,这听起来像很多管道。

【问题讨论】:

    标签: java ajax json spring-mvc controller


    【解决方案1】:

    这不是一个完整的答案,但我希望它能为您指明正确的方向。

    Following 是我们用来从 JSON 到使用 Jackson 的现有对象进行深度绑定的类。这改编自杰克逊的错误报告:https://jira.springsource.org/browse/SPR-10552

    public class JsonBinder
    {
        private ObjectMapper objectMapper;
    
        public JsonBinder( ObjectMapper objectMapper )
        {
            super();
            this.objectMapper = checkNotNull( objectMapper );
        }
    
        public void bind( Object objToBindInto, InputStream jsonStream ) throws JsonProcessingException, IOException
        {
            JsonNode root = objectMapper.readTree( checkNotNull( jsonStream ) );
            applyRecursively( checkNotNull( objToBindInto ), root );
        }
    
        private void applyRecursively( Object objToBindInto, JsonNode node ) throws JsonProcessingException, IOException
        {
            PropertyAccessor propAccessor = null;
    
            for( Iterator<Entry<String, JsonNode>> i = node.fields(); i.hasNext(); )
            {
                Entry<String, JsonNode> fieldEntry = i.next();
                JsonNode child = fieldEntry.getValue();
                if( child.isArray() )
                {
                    // We ignore arrays so they get instantiated fresh every time
                    // root.remove(fieldEntry.getKey());
                }
                else
                {
                    if( child.isObject() )
                    {
                        if( propAccessor == null )
                        {
                            propAccessor = PropertyAccessorFactory.forDirectFieldAccess( objToBindInto );
                        }
                        Object o2 = propAccessor.getPropertyValue( fieldEntry.getKey() );
                        if( o2 != null )
                        {
    
                            // Only remove the JsonNode if the object already exists
                            // Otherwise it will be instantiated when the parent gets
                            // deserialized
                            i.remove();
                            applyRecursively( o2, child );
                        }
                    }
                }
            }
            ObjectReader jsonReader = objectMapper.readerForUpdating( objToBindInto );
            jsonReader.readValue( node );
        }
    }
    

    我们将它与 Spring 的 HandlerMethodArgumentResolver 的实现一起使用。

    我们没有大量使用 Spring 的 MVC 框架。我们只是使用 Spring 的许多不同部分构建 JSON API 后端。让这一切正常运行需要大量的管道,但现在我们的控制器非常简单。

    很遗憾,我无法展示我们所有的代码,反正它很长。我希望这至少可以解决部分问题。

    【讨论】:

      【解决方案2】:

      为什么要用@RequestBody 注释ModelAttribute,在JSON 的情况下,只要有@SessionAttribute 并使用@ModelAttribute 引用该Command 对象就足够了。

      你使用@RequestBody的动机是什么

      http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html

      http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html

      【讨论】:

      • 感谢您的回复。认为为了利用杰克逊在类路径上提供的自动 json 到对象转换,使用 RequestBody 是标准做法。请您指出任何说明 ModelAttribute 也足以处理 JSON 的文档吗?我在另一篇 SOF 帖子上阅读了以下内容:“ModelAttribute 与表单发布参数绑定,RequestBody 将正文直接传递给 Json 转换器”:stackoverflow.com/questions/13229584/…
      猜你喜欢
      • 2013-04-14
      • 2012-04-15
      • 2010-11-25
      • 2018-03-19
      • 2013-08-20
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 2018-05-29
      相关资源
      最近更新 更多