java – 如何在返回多种类型的XML的URL上使用Spring RestTemplate和JAXB编组
|
我需要对一个返回“< job />或者< exception />并始终处于状态码200.(跛脚第三方产品!). 我有如下代码: Job job = getRestTemplate().postForObject(url,postData,Job.class); 我的applicationContext.xml看起来像: <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg ref="httpClientFactory"/>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="jaxbMarshaller"/>
<property name="unmarshaller" ref="jaxbMarshaller"/>
</bean>
<bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>domain.fullspec.Job</value>
<value>domain.fullspec.Exception</value>
</list>
</property>
</bean>
当我尝试使这个电话和服务失败,我得到: Failed to convert value of type 'domain.fullspec.Exception' to required type 'domain.fullspec.Job' 在postForObject()调用中,我要求一个Job.class,而不是得到一个,它正在变得不安. 我想我需要能够做的事情是: Object o = getRestTemplate().postForObject(url,Object.class);
if (o instanceof Job.class) {
...
else if (o instanceof Exception.class) {
}
但是这不行,因为JAXB抱怨说它不知道如何组织到Object.class – 并不奇怪. 我试图创建MarshallingHttpMessageConverter的子类并重写readFromSource() protected Object readFromSource(类clazz,HttpHeaders头,源代码){ Object o = null;
try {
o = super.readFromSource(clazz,headers,source);
} catch (Exception e) {
try {
o = super.readFromSource(MyCustomException.class,source);
} catch (IOException e1) {
log.info("Failed readFromSource "+e);
}
}
return o;
}
不幸的是,这不工作,因为在我重试之前,源代码中的底层输入流已被关闭. 有意见的建议, 汤姆 更新:我已经通过获取一个inputStream的副本来工作 protected Object readFromSource(Class<?> clazz,HttpHeaders headers,Source source) {
InputStream is = ((StreamSource) source).getInputStream();
// Take a copy of the input stream so we can use it for initial JAXB conversion
// and if that fails,we can try to convert to Exception
CopyInputStream copyInputStream = new CopyInputStream(is);
// input stream in source is empty now,so reset using copy
((StreamSource) source).setInputStream(copyInputStream.getCopy());
Object o = null;
try {
o = super.readFromSource(clazz,source);
// we have failed to unmarshal to 'clazz' - assume it is <exception> and unmarshal to MyCustomException
} catch (Exception e) {
try {
// reset input stream using copy
((StreamSource) source).setInputStream(copyInputStream.getCopy());
o = super.readFromSource(MyCustomException.class,source);
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
return o;
}
CopyInputStream取自http://www.velocityreviews.com/forums/t143479-how-to-make-a-copy-of-inputstream-object.html,我将其粘贴到这里. import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class CopyInputStream
{
private InputStream _is;
private ByteArrayOutputStream _copy = new ByteArrayOutputStream();
/**
*
*/
public CopyInputStream(InputStream is)
{
_is = is;
try
{
copy();
}
catch(IOException ex)
{
// do nothing
}
}
private int copy() throws IOException
{
int read = 0;
int chunk = 0;
byte[] data = new byte[256];
while(-1 != (chunk = _is.read(data)))
{
read += data.length;
_copy.write(data,chunk);
}
return read;
}
public InputStream getCopy()
{
return (InputStream)new ByteArrayInputStream(_copy.toByteArray());
}
}
解决方法@Tom:我不认为创建一个自定义的MarshallingHttpMessageConverter会给你带来任何好处.内置的转换器在服务失败时返回正确的类(Exception类),但是RestTemplate不知道如何将Exception类返回给被调用者,因为您已将响应类型指定为Job类.我读了RestTemplate source code,你目前正在调用这个API: public <T> T postForObject(URI url,Object request,Class<T> responseType) throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request,responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType,getMessageConverters());
return execute(url,HttpMethod.POST,requestCallback,responseExtractor);
}
您可以看到,它会根据您的响应类型返回类型T.您可能需要做的是对RestTemplate进行子类化,并添加一个返回Object而不是类型T的新的PostForObject()API,以便您可以对返回的对象执行instanceof检查. UPDATE 我一直在想这个问题的解决方案,而不是使用内置的RestTemplate,为什么不自己写?我认为比子类RestTemplate更好的添加一个新的方法. 这是我的例子…授予,我没有测试这个代码,但它应该给你一个想法: // reuse the same marshaller wired in RestTemplate
@Autowired
private Jaxb2Marshaller jaxb2Marshaller;
public Object genericPost(String url) {
// using Commons HttpClient
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(url);
// add your data here
method.addParameter("data","your-data");
try {
int returnCode = client.executeMethod(method);
// status code is 200
if (returnCode == HttpStatus.SC_OK) {
// using Commons IO to convert inputstream to string
String xml = IOUtil.toString(method.getResponseBodyAsStream());
return jaxb2Marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(xml.getBytes("UTF-8"))));
}
else {
// handle error
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
finally {
method.releaseConnection();
}
return null;
}
如果有些情况需要重新使用RestTemplate中的一些API,那么您可以构建一个适配器来封装您的自定义实现并重用RestTemplate API,而不会在您的代码中实际暴露RestTemplate API. 例如,您可以创建一个适配器接口,如下所示: public interface MyRestTemplateAdapter {
Object genericPost(String url);
// same signature from RestTemplate that you want to reuse
<T> T postForObject(String url,Class<T> responseType,Object... uriVariables);
}
具体的定制休息模板看起来像这样: public class MyRestTemplateAdapterImpl implements MyRestTemplateAdapter {
@Autowired
private RestTemplate restTemplate;
@Autowired
private Jaxb2Marshaller jaxb2Marshaller;
public Object genericPost(String url) {
// code from above
}
public <T> T postForObject(String url,Object... uriVariables) {
return restTemplate.postForObject(url,request,responseType);
}
}
我仍然认为这种方法比使用RestTemplate的子类更清晰,您可以更好地控制如何处理Web服务调用的结果. (编辑:哈尔滨站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
