這是本文件的舊版!


How to fetch raw message after the request is failed? (working)

RESTEasy jackson provider讓我們可以輕鬆的將第三方服務回應的json內容,轉成我們自己的物件,讓程式碼只專注於資源物件的操作;然而,第三方服務並非永遠都如你預期,它可能突然更新甚至吐出你不認得的訊息:

javax.ws.rs.ProcessingException: RESTEASY003145: Unable to find a MessageBodyReader of content-type text/html;charset=iso-8859-1
我有幸遇到這樣的問題,也讓我思考著該如何將原始資料記錄下來以回報第三方服務提供者。本篇文章分享可能的作法,可依照你的需求自行挑選。

ReadInterceptor & InputStreamSniffer

一開始遇到這個問題時,我的想法是: 是否可以透過interceptor去處理? 我所提供的第一個方法,是註冊一個ReadInterceptor,並且wrap原始的InputStream,將讀取的內容給記錄下來。首先是ReaderInterceptor,它會從Context中取出InputStream,並將其wrap成InputStreamSniffer,代整個讀取流程執行後,可以透過getReadContent去拿到讀取的內容:

@Provider
public class SnifferReadInterceptor implements ReaderInterceptor {
 
	private InputStreamSniffer inputStreamSniffer = null;
 
	@Override
	public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
		InputStream inputStream = context.getInputStream();
 
		inputStreamSniffer = new InputStreamSniffer(inputStream);
		context.setInputStream(inputStreamSniffer);
 
		return context.proceed();
	}
 
	public String getReadContent(){
		return inputStreamSniffer.getContent();
	}
}
InputStreamSniffer則是將讀取的動作delegate給原始的InputStream,並悄悄地記下讀取內容:
public class InputStreamSniffer extends InputStream {
 
	private InputStream srcInputStream;
	private StringBuilder contentStringBuilder;
 
	public InputStreamSniffer(InputStream inputStream){
		srcInputStream = inputStream;
		contentStringBuilder = new StringBuilder();
	}
 
	@Override
	public int read() throws IOException {
		int read_c = srcInputStream.read();
		contentStringBuilder.append((char)read_c);
		return read_c;
	}
	// .. skip