1芥炭、問題出現(xiàn)的現(xiàn)象:
在舊版接口平臺產(chǎn)品中發(fā)現(xiàn)原來的計費通過過濾器實現(xiàn),在計費檢查通過后恃慧,請求發(fā)送到servlet進行處理园蝠,然后根據(jù)servlet處理結(jié)果判斷是否需要計費,如果計費失敗則拋出異常(暫不考慮計費流程設(shè)計的瑕疵糕伐,這里僅討論問題及解決方案)
public class FeesFilter extends OncePerRequestFilter {
// 之前有一些計費檢查的邏輯
filterChain.doFilter(request, response);
// 此處進行扣費砰琢,如果費用不足向客戶端輸出錯誤響應(yīng)
if (!consumeFeignResponse.success()) {
response.setCharacterEncoding("UTF-8");
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getOutputStream().println(new String(JacksonUtil.toJson(responseBody).getBytes(), StandardCharsets.ISO_8859_1));
}
}
如果代碼按照現(xiàn)有的邏輯走,會出現(xiàn)以下現(xiàn)象
{
"code": "OK",
"msg": "成功",
"request_id": "1a7decfbe849ea47",
"data": "xxxx"
}
{
"code": "fees.INSUFFICIENT_FEES",
"msg": "費用不足",
"sign_type": "md5",
"request_id": "f604fd519a7f35fc"
}
出現(xiàn)這樣問題的原因是因為在servletresponse接口中有這么一個屬性
/**
* Returns a boolean indicating if the response has been committed. A
* committed response has already had its status code and headers written.
*
* @return a boolean indicating if the response has been committed
* @see #setBufferSize
* @see #getBufferSize
* @see #flushBuffer
* @see #reset
*/
public boolean isCommitted();
當isCommitted=true時,響應(yīng)已經(jīng)提供給客戶端陪汽,所以無法調(diào)用reset()來重置response中的內(nèi)容训唱,再調(diào)用輸出流只會追加輸出內(nèi)容。
response.reset()挚冤;
2况增、解決方案
該方案也是摘抄自網(wǎng)絡(luò),非原創(chuàng)
問題的來源在于在filterChain中傳入的response已經(jīng)寫入了不該寫入的數(shù)據(jù)训挡,所以可以通過包裝類澳骤,使用自定義的字節(jié)流代替原有的字節(jié)流,再根據(jù)邏輯判斷該輸出包裝類里面的字節(jié)流還是另輸出異常信息澜薄。
// 之前過濾鏈可以修改成這樣
ResponseWrapper responseWrapper = new ResponseWrapper(response);
filterChain.doFilter(request, responseWrapper);
// 如果計費失敗使用response輸出異常信息
response.setCharacterEncoding("UTF-8");
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getOutputStream().println(new String(JacksonUtil.toJson(responseBody).getBytes(), StandardCharsets.ISO_8859_1));
// 如果計費成功为肮,輸出包裝類里的接口數(shù)據(jù)
response.getOutputStream().write(responseWrapper.getBuffer());
public class ResponseWrapper extends HttpServletResponseWrapper {
private final HttpServletResponse response;
private final ByteArrayOutputStream bout = new ByteArrayOutputStream();// 字節(jié)流
public ResponseWrapper(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new ResponseWrapperServletOutputStream(bout);
}
public byte[] getBuffer() {
try {
if (pw != null) {
// 如果pw不為空,則我們需要關(guān)閉一下肤京,讓其將數(shù)據(jù)從緩存寫到底層流中去
pw.close();
}
bout.flush();
return bout.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class ResponseWrapperServletOutputStream extends ServletOutputStream {
private final ByteArrayOutputStream bout;
public ResponseWrapperServletOutputStream(ByteArrayOutputStream bout) {
this.bout = bout;
}
@Override
public void write(int arg0) throws IOException {
this.bout.write(arg0);
}
}