问题描述
有一个Java Spring MVC的Web应用程序,和我做一个jQuery的ajax post请求。我的控制器是设置接收和发送JSON数据。一切正常,JSON字符串是格式正确,控制器可以创建并填充Command对象和JSON的请求数据的内容来填充它。不过,我更新数据的联系对象,而我的JSP表单元素只包含了所有必需的DB更新数据的一个子集。在与我取得所有必要的数据从数据库表单JSP页面我最初的GET请求,填充联系Command对象,然后该命令对象绑定到模型。
Have a Java spring MVC web app, and am making a jquery ajax post request. My Controller is setup to receive and send json data. Everything works, the JSON string is well formatted, and the Controller can create and populate a Command object and populate it with the contents of the JSON request data. However, I am updating data for a Contact object, and my JSP form element only contains a subset of all the data required for the DB update. In my initial GET request for the JSP page with the form I retrieve all the necessary data from the DB, populate a Contact Command object, and then bind that command object to the Model.
如果我在做一个正常的POST提交表单提交,我相信只是说出我的命令对象作为@SessionAttribute,并引用在我的onsubmit()POST方法使用@ModelAttribute Command对象就足够了。春天将从我的会议中检索已填充命令对象,然后绑定(覆盖)已更改为POST请求的结果,这些值。这个更新命令对象可随后被用作一个DB更新的参数
If I were doing a normal POST submit form submission, I believe that just declaring my command object as @SessionAttribute, and referencing that Command object using @ModelAttribute in my onSubmit() POST method would be sufficient. Spring would retrieve the already populated command object from my session and then bind (overwrite) those values that have changed as a result of the POST request. This updated command object could then be used as the parameter for a DB update.
不过,我使用Spring 3和利用@RequestBody paramater类型。我不能让春天到两个给我的会话对象并自动从请求绑定新值。它要么给我只是旧的会话命令对象(不应用更改),或一个新的命令对象,只有在POST请求的值。
However, I am using Spring 3 and leveraging @RequestBody paramater type. I cannot get Spring to both give me the session object and automatically bind the new values from the request. It either gives me just the old session command object (without applying the changes) or a new Command Object with only the values from the POST request.
下面是一个小code - 不工作:
Here is a little code - doesn't work:
@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对象与老完全组成和新的数据(以同样的方式很容易使用完整POST HTTP提交实现)。
Can anyone please tell me what is the "standard" or "easiest" way to use @RequestBody with JSON request data and make that bind to an existing / @ModelAttribute populated Command object so that the Command object fully constituted with both old and new data (in the same way it is easily achieved using a full POST http submit).
一个相关的问题是,什么是错的code以上?可以@SessionAttribute和@RequestBody使用JSON内容都可以同时使用?如果是的话,请解释!非常感谢你对任何输入。
A related question is what is wrong with the code above? Can @SessionAttribute and @RequestBody with JSON content all be used together? If so, please explain how! Thank you so much for any input.
我的解决办法是让Spring创建新的命令对象,并自动填充表单数据。然后,让一个独立的呼叫/会话,从手工检索旧的命令对象,最后手动复制所有这些属性没有present的形式提交到新的命令对象。现在,我拥有所有必要的数据集中到一个命令对象申请我的SQL更新使用。必须有一个更简单的方法......;)
My work around is to let Spring create the new Command object and auto-populate with form data. Then make a separate call / retrieve manually from session the old command object, finally manually copy all those attributes that were not present in the form submission into the new command object. Now I have all the necessary data together in one command object to apply my SQL update with. There must be an easier way.... ;)
更新:
今天发现这个SOF岗位的同时,进一步研究这个问题:
Found this SOF post today while further researching this problem:
这似乎没有任何已知的弹簧解决方案开箱即用,但大量的需求要知道处理它的最佳方式。就我而言,是的,我使用嵌套的域对象,以便在后提出的解决办法是不行的。没有人有任何其他的想法?需要明确的是,我想张贴JSON格式的数据控制器(不是简单的HTTP表单提交的数据)。
It appears there is no known SPRING solution out of the box but a lot of demand to know the best way to handle it. In my case, yes, I am using nested domain objects so the workaround offered in the post is no good. Does anyone have any other ideas? To be clear, I wish to POST JSON format data to the Controller (not simply http form post data).
好吧,我已经开了春源JIRA要求为这一个,也许这是一个急需改进:
Ok, I've opened a Spring Source JIRA request for this one, perhaps it is a much needed improvement:
https://jira.springsource.org/browse/SPR-10552
否则,它是利用在巧妙的方法杰克逊转换能力,这听起来像很多水管的情况。
Or else, it is a case of leveraging the Jackson conversion capabilities in clever ways which sounds like a lot of plumbing.
推荐答案
这是不是一个完整的答案,但我希望它会为你指明正确的方向。
This isn't a complete answer, but I hope it will point you in the right direction.
以下是我们用来做深,从JSON结合使用杰克逊现有的对象的类。这是改编自一个bug报告,杰克逊在这里: https://jira.springsource.org/browse/ SPR-10552
Following is a class that we use to do deep binding from JSON to an existing object using Jackson. This is adapted from a bug report for Jackson here: 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 );
}
}
我们用这个伴随着春天的HandlerMethodArgumentResolver的一个实现。
We use this along with a an implementation of Spring's HandlerMethodArgumentResolver.
我们不使用大量的Spring的MVC框架。我们只是建设使用了大量春天的不同部分的JSON API后端。这是管道的pretty的量好把一切的工作,但现在我们的控制器是很简单的。
We don't use a lot of Spring's MVC framework. We are just building a JSON API backend using a lot of different parts of Spring. It is a pretty good amount of plumbing to get it all working, but now our controllers are very simple.
不幸的是,我不能表现出我们所有的code,这是pretty的长期反正。我希望这至少解决了部分问题。
Unfortunately I can't show all of our code, it's pretty long anyways. I hope this solves at least part of the problem.
这篇关于春季3 AJAX POST请求与@RequestBody和@ModelAttribute和@SessionAttribute一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!