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

标签 java ajax json spring-mvc controller

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

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

但是,我正在使用 Spring 3 并利用 @RequestBody 参数类型。我无法让 Spring 既给我 session 对象又自动绑定(bind)来自请求的新值。它要么只给我旧的 session 命令对象(不应用更改),要么给我一个只有来自 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 请求数据,并使其绑定(bind)到现有的/@ModelAttribute 填充的 Command 对象,以便 Command 对象完全由旧的和和新数据(使用完整的 POST http 提交可以轻松实现)。

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

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

更新:

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

Spring Partial Update Object Data Binding

似乎没有开箱即用的已知 SPRING 解决方案,但很多人都要求知道处理它的最佳方法。就我而言,是的,我使用的是嵌套域对象,因此帖子中提供的解决方法并不好。有没有人有任何其他想法?明确地说,我希望将 JSON 格式的数据发布到 Controller (不仅仅是 http 表单发布数据)。

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

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

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

最佳答案

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

以下是我们使用 Jackson 进行从 JSON 到现有对象的深度绑定(bind)的类。这是改编自 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 后端。要使其全部正常工作需要相当多的管道,但现在我们的 Controller 非常简单。

不幸的是我不能展示我们所有的代码,反正它很长。我希望这至少能解决部分问题。

关于java - Spring 3 AJAX POST 请求与@RequestBody 和@ModelAttribute 以及@SessionAttribute 一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16473727/

相关文章:

Java:另一个多线程执行器中的单线程子执行器

Java泛型无法将类型转换为类型

java.util.ConcurrentModificationException问题

Javascript/AJAX 汽车品牌/型号形式

asp.net - 如何在 ASP.Net TextBox 的 Leave 事件上调用服务器端函数

javascript - 下面的json错误在哪里

java - java中unsigned long的等价物是什么

javascript - Google map API 在 safari 下无法正常工作

javascript - map 上未绘制标记

c++,使用nlohmann::json解析JSON数组