Spring 3 AJAX POST请求与@RequestBody和@ModelAttribute和@SessionAttribute一起使用?

2024-01-07

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

如果我正在执行正常的 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 更新。一定有一个更简单的方法......;)

UPDATE:

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

Spring 部分更新对象数据绑定 https://stackoverflow.com/questions/15124858/spring-partial-update-object-data-binding

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

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

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

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


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

以下是我们使用 Jackson 进行从 JSON 到现有对象的深度绑定的类。这是根据 Jackson 的错误报告改编的:https://jira.springsource.org/browse/SPR-10552 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 后端。要使其全部正常工作需要大量的管道,但现在我们的控制器非常简单。

不幸的是我无法展示我们所有的代码,无论如何它都很长。我希望这至少能解决部分问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring 3 AJAX POST请求与@RequestBody和@ModelAttribute和@SessionAttribute一起使用? 的相关文章

随机推荐