Dubbo封装rest服务返回结果

Version:1.0 StartHTML:000000209 EndHTML:000036401 StartFragment:000001944 EndFragment:000036315 StartSelection:000001944 EndSelection:000036309 SourceURL:https://www.cnblogs.com/loveyou/p/9550321.html <title>Dubbo封装rest服务返回结果 - 月·漩涡 - 博客园</title><link href="/bundles/blog-common.css?v=PX31qVjOE47mNaZI9JUSFK-ajuzMnpXA9PeteRNR1Qw1" rel="stylesheet" type="text/css"><link id="MainCss" href="/skins/CodingLife/bundle-CodingLife.css?v=g4Oce5UBaUn_FUwadcT09ICEg5NkULQGtUpNhTtrI8U1" rel="stylesheet" type="text/css"><link href="/blog/customcss/74753.css?v=A9j74Dacv568HcTfEYOA60y0mYQ%3d" rel="stylesheet" type="text/css"><link id="mobile-style" href="/skins/CodingLife/bundle-CodingLife-mobile.css?v=Xay8b9tTSw814nBzbOgvS6rrbcxrobMhvHJHdZAO9vI1" rel="stylesheet" type="text/css" media="only screen and (max-width: 767px)"><link title="RSS" href="http://www.cnblogs.com/loveyou/rss" rel="alternate" type="application/rss+xml"><link title="RSD" href="http://www.cnblogs.com/loveyou/rsd.xml" rel="EditURI" type="application/rsd+xml"><link href="http://www.cnblogs.com/loveyou/wlwmanifest.xml" rel="wlwmanifest" type="application/wlwmanifest+xml"> <script type="text/javascript">var currentBlogApp = 'loveyou', cb_enable_mathjax=false;var isLogined=true;</script>

由于Dubbo服务考虑到一个是给其他系统通过RPC调用,另外一个是提供HTTP协议本身系统的后台管理页面,因此Dubbo返回参数在rest返回的时候配置拦截器进行处理。

在拦截器中,对返回参数封装成如下对象,并统一输出到前端。

image
image

[[图片上传失败...(image-c1cdb4-1535468107320)]](javascript:void(0); "复制代码")

<pre> 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 }</pre>

[[图片上传失败...(image-4b4133-1535468107320)]](javascript:void(0); "复制代码")

View Code

需要继承的服务处理类有(按照实际调用顺序)ExceptionMapper<Exception>, ContainerResponseFilter, WriterInterceptor 。

1. ExceptionMapper<Exception> 用于后台返回异常结果的封装处理,需要对异常类进行区别对待,并返回错误提示信息。

[[图片上传失败...(image-6dd08c-1535468107320)]](javascript:void(0); "复制代码")

<pre>/** * 异常拦截 */ @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();
}</pre>

[[图片上传失败...(image-e2a950-1535468107320)]](javascript:void(0); "复制代码")

2.ContainerResponseFilter 可用于服务端返回状态码进行处理,由于方法返回类型是 void,或者某个资源类型返回是 null 的情况,JAX-RS 框架一般会返回状态204,表示请求处理成功但没有响应内容。因此在此处我们对返回值封装成200-处理成功,数据为空的情况。

[[图片上传失败...(image-58054e-1535468107320)]](javascript:void(0); "复制代码")

<pre>@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);
    }
}</pre>

[[图片上传失败...(image-94fb96-1535468107320)]](javascript:void(0); "复制代码")

3.WriterInterceptor 用于服务端返回值的写入处理,进入到此处的一般都是执行成功的返回值。

[[图片上传失败...(image-f57fca-1535468107320)]](javascript:void(0); "复制代码")

<pre>@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();

}</pre>

[[图片上传失败...(image-da71b3-1535468107320)]](javascript:void(0); "复制代码")

以上代码即完成了返回值的封装,如果有其他异常类型还需要特殊处理,在ExceptionMapper增加异常判断。

完成代码如下:

image
image

<pre>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());
}

}

}</pre>

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

<pre>*org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:209)
org.jboss.resteasy.core.SynchronousDispatcher.lambdainvoke4(SynchronousDispatcher.java:230)
org.jboss.resteasy.core.SynchronousDispatcher.lambdapreprocess0(SynchronousDispatcher.java:139)

这种情况一般是有什么特殊异常,在toResponse中没有进行特殊处理,Response中.entity(result)放入了返回对象无法解析造成。处理方式参考(e instanceof javax.ws.rs.NotFoundException)增加异常的特殊处理即可。*</pre>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,577评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • 1、潜意识接收的是词汇和经由词汇所自然产生的联想 我们从小就接受谦虚是美德的教育,从而不敢使用强有力的词汇,不敢说...
    helenliu258阅读 3,704评论 0 4
  • 自从跟前任分手后,经常想起前前任,也是第一个女朋友。因为从小好学生,乖孩子,听爸妈话,好好学习,不谈恋爱,标准的好...
    ingularityPoooi阅读 71评论 0 0