Dubbo封装rest服务返回结果
由于Dubbo服务考虑到一个是给其他系统通过RPC调用,另外一个是提供HTTP协议本身系统的后台管理页面,因此Dubbo返回参数在rest返回的时候配置拦截器进行处理。
在拦截器中,对返回参数封装成如下对象,并统一输出到前端。
package com.wjs.common.web; import org.apache.commons.lang.builder.ReflectionToStringBuilder; /**
* 服务返回给客户端的json对象包装
*/
public class JsonResult<T> { private boolean success = false; private String resultMsg = ""; private T data = null; public JsonResult(boolean status, String resultMsg) {
this.success = status;
this.resultMsg = resultMsg;
} public JsonResult(boolean status, String resultMsg, T data) {
this.success = status;
this.resultMsg = resultMsg;
this.data = data;
} public boolean isSuccess() { return success;
} public void setSuccess(boolean success) { this.success = success;
} public String getResultMsg() { return resultMsg;
} public void setResultMsg(String resultMsg) { this.resultMsg = resultMsg;
} public T getData() { return data;
} public void setData(T data) { this.data = data;
} /*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
}
需要继承的服务处理类有(按照实际调用顺序)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());
} } }
异常处理的时候,比较容易出现异常
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)增加异常的特殊处理即可。
Dubbo封装rest服务返回结果的更多相关文章
- Dubbo中暴露服务的过程解析
dubbo暴露服务有两种情况,一种是设置了延迟暴露(比如delay="5000"),另外一种是没有设置延迟暴露或者延迟设置为-1(delay="-1"): 设置 ...
- 【DUBBO】Dubbo原理解析-服务引用
服务引用是服务的消费方向注册中心订阅服务提供方提供的服务地址后向服务提供方引用服务的过程. 服务的应用方在spring的配置实例如下: <dubbo:referenceid="demo ...
- Dubbo消费方服务调用过程源码分析
参考:dubbo消费方服务调用过程源码分析dubbo基于spring的构建分析Dubbo概述--调用过程dubbo 请求调用过程分析dubbo集群容错机制代码分析1dubbo集群容错策略的代码分析2d ...
- dubbo入门之服务消费
今天,我们来看看dubbo消费的执行过程 首先,我们都知道dubbo是一个基于netty实现的RPC框架,底层通信是使用netty来实现的.在学习dubbo的时候,或许我们都会有下面的这些疑惑: 1. ...
- REST服务返回自定义的HttpResponseMessage
WebApi框架中对资源的操作,都是通过其Controller提供的各种方法(GET,POST,PUT,DELET等)来实现,而这些方法的返回信息有以下几种形式: 方法返回类型 HttpRespons ...
- AngularJS 事件广播与接收 $broadcast,$emit,$on 作用域间通信 封装factory服务 发布订阅
不同作用域之间通过组合使用$broadcast,$emit,$on的事件广播机制来进行通信. 一.说明 1.广播 $broadcast 说明:将事件从父级作用域传播至本作用域及子级作用域. 格式:$b ...
- Spring Boot Dubbo 构建分布式服务
概述: 节点角色说明 节点 角色说明 Provider 暴露服务的服务提供方 Consumer 调用远程服务的服务消费方 Registry 服务注册与发现的注册中心 Monitor 统计服务的调用次数 ...
- 传统保险企业基于 Dubbo 的微服务实践
本文整理自中国人寿保险(海外)股份有限公司深圳中心技术总监家黄晓彬在 Dubbo 社区开发者日深圳站的现场分享. 中国人寿保险(海外)股份有限公司负责香港.澳门.新加坡和印尼的业务开发,和国内业务不同 ...
- springcloud中使用dubbo开发rpc服务及调用
spring cloud中基于springboot开发的微服务,是基于http的rest接口,也可以开发基于dubbo的rpc接口. 一,创建goodsService模块 1, 在创建的goodsSe ...
随机推荐
- 2010浙大:zoj问题
题目描述: 对给定的字符串(只包含'z','o','j'三种字符),判断他是否能AC. 是否AC的规则如下:1. zoj能AC:2. 若字符串形式为xzojx,则也能AC,其中x可以是N个'o' 或者 ...
- POJ3928(树状数组:统计数字出现个数)
Ping pong Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 2641 Accepted: 978 Descript ...
- CodeForces 1118F2. Tree Cutting (Hard Version)
题目简述:给定$n \leq 3 \times 10^5$个节点的树,其中一部分节点被染色,一共有$k$种不同的颜色.求将树划分成 $k$ 个不相交的部分的方案数,使得每个部分中除了未染色的节点以外的 ...
- win32 API中GetSystemMetrics函数
1. SM_ARRANGE: 用于说明系统如何安排最小化窗口,根据显示器的不同系统数据可能有所不同.其包含一个起始位置和方向.关于在程序中怎么使用我还没有见个这样的代码. 起始位置可为下列值之一: A ...
- HRBUST - 1819 石子合并问题--圆形版(区间dp+环形+四边形优化)
石子合并问题--圆形版 在圆形操场上摆放着一行共n堆的石子.现要将石子有序地合并成一堆.规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆石子数记为该次合并的得分.请编辑计算出将n堆石子合并成一堆的 ...
- HDU - 2962 Trucking SPFA+二分
Trucking A certain local trucking company would like to transport some goods on a cargo truck from o ...
- jdbcTemplate简单使用
package com.bizvane.spider.tools; import org.apache.commons.dbcp.BasicDataSource; import org.springf ...
- SCUT - 278 - 谜题#020 - 左偏树
https://scut.online/p/278 第一次遇到不需要并查集的左偏树. #include<bits/stdc++.h> using namespace std; typede ...
- IDEA如何找到接口的实现类
如何找到接口的实现类 (IDEA))在ApplicationContext上右击 Diagrams ->show diagram 可以看到继承关系: 在ApplicationContext上右击 ...
- Insert into a Binary Search Tree
Given the root node of a binary search tree (BST) and a value to be inserted into the tree, insert t ...