• Dubbo封装rest服务返回结果


    由于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());
            }
    
        }
    
    }
    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-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)增加异常的特殊处理即可。
  • 相关阅读:
    module 和 component 的区别
    API、SDK、DLL有什么用?
    app基本控件
    PaaS是什么?
    js回调函数(callback)(转载)
    多语言 SEO
    axure rp 8.0
    整天看用户埋点数据,知道数据是咋来的吗?
    发现恶意ip大量访问 可使用命令进行封禁
    阿里云服务器迁移更改IP,导致网站挂掉
  • 原文地址:https://www.cnblogs.com/loveyou/p/9550321.html
Copyright © 2020-2023  润新知