Spring MVC - 将 XML 参数编组到多部分请求中的对象 (JAXB)
我使用带有 apache commons 多部分解析器支持的 Spring MVC 创建了一个文件上传服务,该服务指定应将文件作为多部分 HTTP Post 请求的一部分附加。该请求还包含一个参数,其中包含带有有关该对象的元数据的 XML 字符串。可以使用 JAXB 来编组 XML。
其他不是多部分的服务透明地处理编组,例如:
@RequestMapping(value = "/register", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public ModelAndView createUser(@RequestBody CreateUserDTO createUserDTO) throws Exception {
UserDTO user = userService.createUser(createUserDTO);
return createModelAndView(user);
}
这里 CreateUserDTO 是一个自动编组的 JAXB 注释对象。 在多部分情况下,我希望具有相同的透明度。理想情况下,我想做以下事情:
RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public ModelAndView createAttachment(@RequestParam AttachmentDTO attachment,
HttpServletRequest request) throws Exception {
final MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
AttachmentDTO attachment = null;
final MultipartFile dataFile = multipartRequest.getFile("data");
AttachmentDTO createdAttachment = attachmentService.createAttachment(attachment,
dataFile);
return createModelAndView(createdAttachment);
}
不幸的是,这不起作用。我可以将附件参数绑定为字符串,但自动编组不起作用。我的解决办法是手动进行如下编组,但我不喜欢这种方法(特别是因为参数可能以 JSON 和 XML 形式指定):
@Autowired
private Jaxb2Marshaller jaxb2Marshaller;
@Autowired
private ObjectMapper jacksonMapper;
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public ModelAndView createAttachment(@RequestParam(ATTACHMENT_PARAMETER) String attachmentString,
final HttpServletRequest request) throws Exception {
final MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
AttachmentDTO attachment = null;
try {
attachment = (AttachmentDTO)jaxb2Marshaller.unmarshal(new StreamSource(new StringReader(attachmentString)));
} catch (XmlMappingException e) {
//Try JSON
try {
attachment = jacksonMapper.readValue(attachmentString, AttachmentDTO.class);
} catch (IOException e1) {
throw new BadRequestException("Could not interpret attachment parameter, both JSON and XML parsing failed");
}
}
有谁对解决此问题有更好的建议吗?
为了完整起见,我还在这里指定了相关的 Spring 配置:
<bean id="viewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1"/>
<property name="favorPathExtension" value="true"/>
<property name="ignoreAcceptHeader" value="false"/>
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="modelKey" value="object"/>
<property name="marshaller" ref="jaxbMarshaller"/>
</bean>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
<property name="objectMapper" ref="jaxbJacksonObjectMapper"/>
</bean>
</list>
</property>
</bean>
<!--Use JAXB OXM marshaller to marshall/unmarshall following class-->
<bean id="jaxbMarshaller"
class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.behindmedia.btfd.model.dto"/>
</bean>
<bean id="jaxbJacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>
<!-- allows for integration of file upload functionality -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<property name="maxUploadSize" value="100000000"/>
</bean>
I created a file upload service using Spring MVC with apache commons multipart resolver support which specifies that a file should be attached as part of a multipart HTTP Post request. The request also contains a parameter containing an XML string with meta-data about the object. The XML can be marshalled using JAXB.
Other services that are not multipart handle the marshalling transparently, e.g.:
@RequestMapping(value = "/register", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public ModelAndView createUser(@RequestBody CreateUserDTO createUserDTO) throws Exception {
UserDTO user = userService.createUser(createUserDTO);
return createModelAndView(user);
}
Here CreateUserDTO is a JAXB annotated object which is automatically marshalled.
In the multipart case I'd like to have the same transparency. Ideally I would like to do the following:
RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public ModelAndView createAttachment(@RequestParam AttachmentDTO attachment,
HttpServletRequest request) throws Exception {
final MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
AttachmentDTO attachment = null;
final MultipartFile dataFile = multipartRequest.getFile("data");
AttachmentDTO createdAttachment = attachmentService.createAttachment(attachment,
dataFile);
return createModelAndView(createdAttachment);
}
Unfortunately this does not work. I am able to bind the attachment parameter as String, but the automatic marshalling does not work. My work around is to manually do the marshalling like the following, but I don't like this approach (especially since the parameter may be specified both in JSON and XML form):
@Autowired
private Jaxb2Marshaller jaxb2Marshaller;
@Autowired
private ObjectMapper jacksonMapper;
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public ModelAndView createAttachment(@RequestParam(ATTACHMENT_PARAMETER) String attachmentString,
final HttpServletRequest request) throws Exception {
final MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
AttachmentDTO attachment = null;
try {
attachment = (AttachmentDTO)jaxb2Marshaller.unmarshal(new StreamSource(new StringReader(attachmentString)));
} catch (XmlMappingException e) {
//Try JSON
try {
attachment = jacksonMapper.readValue(attachmentString, AttachmentDTO.class);
} catch (IOException e1) {
throw new BadRequestException("Could not interpret attachment parameter, both JSON and XML parsing failed");
}
}
Does anyone have a better suggestion for resolving this issue?
For completeness I also specify the relevant Spring config here:
<bean id="viewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1"/>
<property name="favorPathExtension" value="true"/>
<property name="ignoreAcceptHeader" value="false"/>
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="modelKey" value="object"/>
<property name="marshaller" ref="jaxbMarshaller"/>
</bean>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
<property name="objectMapper" ref="jaxbJacksonObjectMapper"/>
</bean>
</list>
</property>
</bean>
<!--Use JAXB OXM marshaller to marshall/unmarshall following class-->
<bean id="jaxbMarshaller"
class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.behindmedia.btfd.model.dto"/>
</bean>
<bean id="jaxbJacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>
<!-- allows for integration of file upload functionality -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<property name="maxUploadSize" value="100000000"/>
</bean>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论