Tom*_*Tom 13 java jaxb resttemplate
我需要对返回a <job/>或an <exception/>和always状态代码的服务进行Rest POST 200.(蹩脚的第三方产品!).
我的代码如下:
Job job = getRestTemplate().postForObject(url, postData, Job.class);
Run Code Online (Sandbox Code Playgroud)
我的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>
Run Code Online (Sandbox Code Playgroud)
当我尝试拨打此电话并且服务失败时,我得到:
Failed to convert value of type 'domain.fullspec.Exception' to required type 'domain.fullspec.Job'
Run Code Online (Sandbox Code Playgroud)
在postForObject()调用中,我要求一个Job.class而不是一个,它正在变得烦恼.
我想我需要能够做一些事情:
Object o = getRestTemplate().postForObject(url, postData, Object.class);
if (o instanceof Job.class) {
...
else if (o instanceof Exception.class) {
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为JAXB抱怨它不知道如何编组到Object.class - 这并不奇怪.
我试图创建MarshallingHttpMessageConverter的子类并覆盖readFromSource()
protected Object readFromSource(Class clazz,HttpHeaders headers,Source source){
Object o = null;
try {
o = super.readFromSource(clazz, headers, source);
} catch (Exception e) {
try {
o = super.readFromSource(MyCustomException.class, headers, source);
} catch (IOException e1) {
log.info("Failed readFromSource "+e);
}
}
return o;
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,这不起作用,因为在我重试它时,源内的基础输入流已经关闭.
感激地收到任何建议,
汤姆
更新:我通过获取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, headers, 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, headers, source);
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
return o;
}
Run Code Online (Sandbox Code Playgroud)
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, 0, chunk);
}
return read;
}
public InputStream getCopy()
{
return (InputStream)new ByteArrayInputStream(_copy.toByteArray());
}
}
Run Code Online (Sandbox Code Playgroud)
@Tom:我认为创建自定义 MarshallingHttpMessageConverter 对您没有任何好处。当服务失败时,内置转换器会向您返回正确的类(异常类),但它RestTemplate不知道如何将异常类返回给被调用者,因为您已将响应类型指定为 Job 类。
我阅读了RestTemplate 源代码,您目前正在调用此 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);
}
Run Code Online (Sandbox Code Playgroud)
如您所见,它根据您的响应类型返回类型 T。您可能需要做的是子类化RestTemplate并添加一个postForObject()返回 Object 而不是类型 T的新API,以便您可以instanceof对返回的对象执行检查。
更新
一直在思考这个问题的解决方案,而不是使用内置的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;
}
Run Code Online (Sandbox Code Playgroud)
如果在某些情况下您想要重用 中的某些 API RestTemplate,您可以构建一个适配器来包装您的自定义实现并重用RestTemplateAPI,而无需RestTemplate在您的代码中实际公开API。
例如,您可以创建一个适配器接口,如下所示:-
public interface MyRestTemplateAdapter {
Object genericPost(String url);
// same signature from RestTemplate that you want to reuse
<T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables);
}
Run Code Online (Sandbox Code Playgroud)
具体的自定义休息模板看起来像这样:-
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 request, Class<T> responseType, Object... uriVariables) {
return restTemplate.postForObject(url, request, responseType);
}
}
Run Code Online (Sandbox Code Playgroud)
我仍然认为这种方法比子类化要干净得多,RestTemplate并且您可以更好地控制如何处理来自 Web 服务调用的结果。
在尝试解决同样的问题时,我找到了以下解决方案.
我正在使用RestTemplate的默认实例,并使用xjc生成文件.调用的转换器是Jaxb2RootElementHttpMessageConverter.
事实证明,如果输入类使用XmlRootElement批注进行批注,转换器将返回"real"类型.也就是说,该方法
protected Object readFromSource(Class clazz, HttpHeaders headers, Source source)
Run Code Online (Sandbox Code Playgroud)
可能会返回一个不是clazz实例的Object,因为clazz中存在一个XmlRootElement注释.在这种情况下,clazz仅用于创建一个unmarshaller来解组clazz.
以下技巧解决了问题:如果我们定义
@XmlRootElement()
@XmlSeeAlso({ Exception.class, Job.class })
public static abstract class XmlResponse {}
Run Code Online (Sandbox Code Playgroud)
并将XmlResponse.class传递给postForObject(...),而响应将是Exception或Job.
这有点像黑客,但它解决了postForObject方法无法返回多个对象类的问题.
| 归档时间: |
|
| 查看次数: |
34919 次 |
| 最近记录: |