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-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增加异常判断。
完成代码如下:
<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.lambda4(SynchronousDispatcher.java:230)
org.jboss.resteasy.core.SynchronousDispatcher.lambda0(SynchronousDispatcher.java:139)
这种情况一般是有什么特殊异常,在toResponse中没有进行特殊处理,Response中.entity(result)放入了返回对象无法解析造成。处理方式参考(e instanceof javax.ws.rs.NotFoundException)增加异常的特殊处理即可。*</pre>