由于Dubbo服務考慮到一個是給其他系統通過RPC調用,另外一個是提供HTTP協定本身系統的背景管理頁面,是以Dubbo傳回參數在rest傳回的時候配置攔截器進行處理。
在攔截器中,對傳回參數封裝成如下對象,并統一輸出到前端。

1 package com.wjs.common.web;
2
3 import org.apache.commons.lang.builder.ReflectionToStringBuilder;
4
5 /**
6 * 服務傳回給用戶端的json對象包裝
7 */
8 public class JsonResult<T> {
9
10 private boolean success = false;
11
12 private String resultMsg = "";
13
14 private T data = null;
15
16 public JsonResult(boolean status, String resultMsg) {
17 this.success = status;
18 this.resultMsg = resultMsg;
19 }
20
21 public JsonResult(boolean status, String resultMsg, T data) {
22 this.success = status;
23 this.resultMsg = resultMsg;
24 this.data = data;
25 }
26
27
28
29 public boolean isSuccess() {
30
31 return success;
32 }
33
34
35 public void setSuccess(boolean success) {
36
37 this.success = success;
38 }
39
40
41 public String getResultMsg() {
42
43 return resultMsg;
44 }
45
46
47 public void setResultMsg(String resultMsg) {
48
49 this.resultMsg = resultMsg;
50 }
51
52
53 public T getData() {
54
55 return data;
56 }
57
58
59 public void setData(T data) {
60
61 this.data = data;
62 }
63
64 /*
65 * (non-Javadoc)
66 *
67 * @see java.lang.Object#toString()
68 */
69 @Override
70 public String toString() {
71 return ReflectionToStringBuilder.toString(this);
72 }
73 }
View Code
需要繼承的服務處理類有(按照實際調用順序)ExceptionMapper<Exception>, ContainerResponseFilter, WriterInterceptor 。
1. ExceptionMapper<Exception> 用于背景傳回異常結果的封裝處理,需要對異常類進行差別對待,并傳回錯誤提示資訊。
/**
* 異常攔截
*/
@Override
public Response toResponse(Exception e) {
// System.err.println("進入結果處理——toResponse");
String errMsg = e.getMessage();
JsonResult<Object> result = new JsonResult<>(false, StringUtils.isEmpty(errMsg)? "系統異常" : errMsg);
if(javax.ws.rs.ClientErrorException.class.isAssignableFrom(e.getClass())){
ClientErrorException ex = (ClientErrorException) e;
LOGGER.error("請求錯誤:" + e.getMessage());
return ex.getResponse();
}
if(e instanceof BaseException){ // 背景自定義異常,用于傳遞異常參數
BaseException ex = (BaseException) e;
result.setData(ex.getErrorParams());
}
LOGGER.error(errMsg, e);
return Response.status(200).entity(result).build();
}
2.ContainerResponseFilter 可用于服務端傳回狀态碼進行處理,由于方法傳回類型是 void,或者某個資源類型傳回是 null 的情況,JAX-RS 架構一般會傳回狀态204,表示請求處理成功但沒有響應内容。是以在此處我們對傳回值封裝成200-處理成功,資料為空的情況。
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
// System.err.println("進入結果處理——filter");
// 它的目的是專門處理方法傳回類型是 void,或者某個資源類型傳回是 null 的情況,
// 這種情況下JAX-RS 架構一般會傳回狀态204,表示請求處理成功但沒有響應内容。 我們對這種情況也重新處理改為操作成功
String wrapTag = requestContext.getProperty("Not-Wrap-Result") == null ? "" : requestContext.getProperty("Not-Wrap-Result").toString(); // 用戶端顯示提醒不要對傳回值進行封裝
if (StringUtils.isBlank(wrapTag) &&responseContext.getStatus() == 204 && !responseContext.hasEntity()){
responseContext.setStatus(200);
responseContext.setEntity(new JsonResult<>(true, "執行成功"));
responseContext.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
}
}
3.WriterInterceptor 用于服務端傳回值的寫入處理,進入到此處的一般都是執行成功的傳回值。
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
// System.err.println("進入結果處理——aroundWriteTo");
// 針對需要封裝的請求對結構進行封裝處理。這裡需要注意的是對傳回類型已經是封裝類(比如:異常處理器的響應或者204處理可能已經是封裝類型)時要忽略掉。
Object originalObj = context.getEntity();
String wrapTag = context.getProperty("Not-Wrap-Result") == null ? "" : context.getProperty("Not-Wrap-Result").toString(); // 用戶端通過head參數顯示提醒不要對傳回值進行封裝
Boolean wraped = originalObj instanceof JsonResult; // 已經被封裝過了的,不用再次封裝
if (StringUtils.isBlank(wrapTag) && !wraped){
JsonResult<Object> result = new JsonResult<>(true, "執行成功");
result.setData(context.getEntity());
context.setEntity(result);
// 以下兩處set避免出現Json序列化的時候,對象類型不符的錯誤
context.setType(result.getClass());
context.setGenericType(result.getClass().getGenericSuperclass());
}
context.proceed();
}
以上代碼即完成了傳回值的封裝,如果有其他異常類型還需要特殊處理,在ExceptionMapper增加異常判斷。
完成代碼如下:

package com.wjs.member.plugin.intercepter;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import org.apache.commons.lang.StringUtils;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.wjs.common.exception.BaseException;
import com.wjs.common.web.JsonResult;
@Provider
public class RestContextInteceptor implements ContainerRequestFilter, WriterInterceptor, ContainerResponseFilter, ExceptionMapper<Exception> {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceExecutionInterceptor.class);
private static final String ENCODING_UTF_8 = "UTF-8";
@Context
private HttpServletRequest request;
@Context
private HttpServletResponse response;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// System.err.println("進入請求攔截——filter");
// 編碼處理
request.setCharacterEncoding(ENCODING_UTF_8);
response.setCharacterEncoding(ENCODING_UTF_8);
request.setAttribute(InputPart.DEFAULT_CHARSET_PROPERTY, ENCODING_UTF_8);
requestContext.setProperty(InputPart.DEFAULT_CHARSET_PROPERTY, ENCODING_UTF_8);
// 用戶端head顯示提醒不要對傳回值進行封裝
requestContext.setProperty("Not-Wrap-Result", requestContext.getHeaderString("Not-Wrap-Result") == null ? "" : requestContext.getHeaderString("Not-Wrap-Result"));
// 請求參數列印
logRequest(request);
}
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
// System.err.println("進入結果處理——aroundWriteTo");
// 針對需要封裝的請求對結構進行封裝處理。這裡需要注意的是對傳回類型已經是封裝類(比如:異常處理器的響應可能已經是封裝類型)時要忽略掉。
Object originalObj = context.getEntity();
String wrapTag = context.getProperty("Not-Wrap-Result") == null ? "" : context.getProperty("Not-Wrap-Result").toString(); // 用戶端顯示提醒不要對傳回值進行封裝
Boolean wraped = originalObj instanceof JsonResult; // 已經被封裝過了的,不用再次封裝
if (StringUtils.isBlank(wrapTag) && !wraped){
JsonResult<Object> result = new JsonResult<>(true, "執行成功");
result.setData(context.getEntity());
context.setEntity(result);
// 以下兩處set避免出現Json序列化的時候,對象類型不符的錯誤
context.setType(result.getClass());
context.setGenericType(result.getClass().getGenericSuperclass());
}
context.proceed();
}
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
// System.err.println("進入結果處理——filter");
// 它的目的是專門處理方法傳回類型是 void,或者某個資源類型傳回是 null 的情況,
// 這種情況下JAX-RS 架構一般會傳回狀态204,表示請求處理成功但沒有響應内容。 我們對這種情況也重新處理改為操作成功
String wrapTag = requestContext.getProperty("Not-Wrap-Result") == null ? "" : requestContext.getProperty("Not-Wrap-Result").toString(); // 用戶端顯示提醒不要對傳回值進行封裝
if (StringUtils.isBlank(wrapTag) &&responseContext.getStatus() == 204 && !responseContext.hasEntity()){
responseContext.setStatus(200);
responseContext.setEntity(new JsonResult<>(true, "執行成功"));
responseContext.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
}
}
/**
* 異常攔截
*/
@Override
public Response toResponse(Exception e) {
// System.err.println("進入結果處理——toResponse");
String errMsg = e.getMessage();
JsonResult<Object> result = new JsonResult<>(false, StringUtils.isEmpty(errMsg)? "系統異常" : errMsg);
if(javax.ws.rs.ClientErrorException.class.isAssignableFrom(e.getClass())){
ClientErrorException ex = (ClientErrorException) e;
LOGGER.error("請求錯誤:" + e.getMessage());
return ex.getResponse();
}
if(e instanceof BaseException){
BaseException ex = (BaseException) e;
result.setData(ex.getErrorParams());
}
LOGGER.error(errMsg, e);
return Response.status(200).entity(result).build();
}
private void logRequest(HttpServletRequest request) {
StringBuffer logBuffer = new StringBuffer(128);
// // refer_url
// logBuffer.append("referUrl:");
//
// @SuppressWarnings("rawtypes")
// Enumeration e = request.getHeaders("Referer");
// String referUrl;
// if (e.hasMoreElements()) {
// referUrl = (String) e.nextElement();
// } else {
// referUrl = "直接通路";
// }
// logBuffer.append(referUrl);
// 擷取url
logBuffer.append(";URL:");
StringBuffer url = request.getRequestURL();
if (url != null) {
StringUtils.replaceOnce(url.toString(), "http://", "https://");
}
logBuffer.append(url.toString());
// 判斷使用者請求方式是否為ajax
logBuffer.append(";ISAJAX:");
String requestType = request.getHeader("X-Requested-With");
if (StringUtils.isNotBlank(requestType) && requestType.equals("XMLHttpRequest")) {
logBuffer.append("true");
} else {
logBuffer.append("false");
}
//擷取所有參數
StringBuffer paramBuffer = new StringBuffer(64);
Enumeration<?> enu = request.getParameterNames();
while (enu.hasMoreElements()) {
String paraName = (String) enu.nextElement();
paramBuffer.append(paraName);
paramBuffer.append(": ");
paramBuffer.append(request.getParameter(paraName));
paramBuffer.append(", ");
}
logBuffer.append(";Parameters:");
logBuffer.append(paramBuffer.toString());
// 記錄本次請求耗時:
// Long requestEndTime = System.currentTimeMillis();
// Long requestStartTime = StringUtils.isEmpty(MDC.get(REQUEST_STARTTIME)) ? requestEndTime : Long.valueOf(MDC.get(REQUEST_STARTTIME));
// logBuffer.append(";COST:");
// logBuffer.append(requestEndTime - requestStartTime);
// logBuffer.append(" ms");
if (!"HEAD".equals(request.getMethod())) {
LOGGER.info("requestLog:" + logBuffer.toString());
}
}
}
異常處理的時候,比較容易出現異常
org.jboss.resteasy.spi.UnhandledException: org.jboss.resteasy.core.NoMessageBodyWriterFoundFailure: Could not find MessageBodyWriter for response object of type: com.wjs.common.web.JsonResult of media type: text/html;charset=UTF-8
org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:209)
org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:230)
org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:139)
這種情況一般是有什麼特殊異常,在toResponse中沒有進行特殊處理,Response中.entity(result)放入了傳回對象無法解析造成。處理方式參考(e instanceof javax.ws.rs.NotFoundException)增加異常的特殊處理即可。