Dubbo封裝rest服務返回結果

因爲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 }
View Code

 

須要繼承的服務處理類有(按照實際調用順序)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());
        }

    }

}
View Code

 

異常處理的時候,比較容易出現異常框架

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)增長異常的特殊處理便可。
相關文章
相關標籤/搜索