因爲Dubbo服務考慮到一個是給其餘系統經過RPC調用,另一個是提供HTTP協議自己系統的後臺管理頁面,所以Dubbo返回參數在rest返回的時候配置攔截器進行處理。html
在攔截器中,對返回參數封裝成以下對象,並統一輸出到前端。前端
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 }
須要繼承的服務處理類有(按照實際調用順序)ExceptionMapper<Exception>, ContainerResponseFilter, WriterInterceptor 。java
1. ExceptionMapper<Exception> 用於後臺返回異常結果的封裝處理,須要對異常類進行區別對待,並返回錯誤提示信息。web
/** * 異常攔截 */ @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-處理成功,數據爲空的狀況。ajax
@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 用於服務端返回值的寫入處理,進入到此處的通常都是執行成功的返回值。apache
@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增長異常判斷。json
完成代碼以下:app
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-8ide
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)增長異常的特殊處理便可。