IDEA项目搭建十三——服务消费端与生产端通信实现
一、简介
之前已经完成了EurekaClient的服务生产者和Feign的服务消费者模块的搭建,现在实现统一的通信约定
(1) 统一Request结构
(2) 统一Response结构
(3) 统一Error通知
二、代码
1、创建统一请求对象ServiceRequest<>实际参数就是这个泛型,使用统一的构造进行创建便于对数据进行统一的加密传输
import java.util.Date; /**
* 客户端请求内容对象
*/
public class ServiceRequest<T> { //region 属性 /**
* 请求唯一ID
*/
private String requestID;
/**
* 请求时间
*/
private Date requestTime;
/**
* 客户端代号
*/
private String clientCode;
/**
* 请求签名
*/
private String requestSign;
/**
* 请求参数
*/
private T reqData; public String getRequestID() {
return requestID;
} public void setRequestID(String requestID) {
this.requestID = requestID;
} public Date getRequestTime() {
return requestTime;
} public void setRequestTime(Date requestTime) {
this.requestTime = requestTime;
} public String getClientCode() {
return clientCode;
} public void setClientCode(String clientCode) {
this.clientCode = clientCode;
} public String getRequestSign() {
return requestSign;
} public void setRequestSign(String requestSign) {
this.requestSign = requestSign;
} /**
* 禁止使用此方法-不能删除内部自动取值需要使用
* @return
*/
@Deprecated
public T getReqData() {
return reqData;
} /**
* 禁止使用此方法-不能删除内部自动赋值需要使用
* @param reqData
*/
@Deprecated
public void setReqData(T reqData) {
this.reqData = reqData;
} //endregion //设置类的无参构造为私有禁止外部实例化
private ServiceRequest() {
} /**
* 创建请求对象
* @param data
*/
public ServiceRequest(T data) {
//后期从此处增加加解密代码...
this.reqData = data;
} /**
* 获取请求参数
* @param req
* @param <T>
* @return
*/
public static <T> T getRequestData(ServiceRequest<T> req) {
//后期从此处增加加解密代码...
T obj = req.getReqData();
return obj;
}
}
2、创建统一响应对象ServiceResponse<>实际响应就是这个泛型,使用统一的取值便于有需求时对响应数据进行统一的解密
import com.google.common.base.Strings;
import java.util.Date; /**
* 服务端响应结果对象
*/
public class ServiceResponse<T> { //region 属性 /**
* 请求唯一ID
*/
private String requestID;
/**
* 响应代号
* <p>
* 000000 - 正确
*/
private ServiceCodeMsgEnum resCodeMsg;
/**
* 响应时间
*/
private Date resTime;
/**
* 响应结果
*/
private T resData; public String getRequestID() {
return requestID;
} public void setRequestID(String requestID) {
this.requestID = requestID;
} public ServiceCodeMsgEnum getResCodeMsg() {
return resCodeMsg;
} public void setResCodeMsg(ServiceCodeMsgEnum resCodeMsg) {
this.resCodeMsg = resCodeMsg;
} public Date getResTime() {
return resTime;
} public void setResTime(Date resTime) {
this.resTime = resTime;
} /**
* 禁止使用此方法-不能删除内部取值需要使用
*
* @return
*/
@Deprecated
public T getResData() {
return resData;
} /**
* 禁止使用此方法-不能删除内部赋值需要使用
*
* @param resData
*/
@Deprecated
public void setResData(T resData) {
this.resData = resData;
} //endregion //设置类的无参构造为私有禁止外部实例化,只能通过下方静态方法创建
private ServiceResponse() {
} /**
* 创建执行正确响应对象
* @param data
*/
public ServiceResponse(T data) {
this.resCodeMsg = ServiceCodeMsgEnum.Success;
this.resData = data;
} /**
* 创建执行错误响应对象
* @param codeMsg
*/
public ServiceResponse(ServiceCodeMsgEnum codeMsg) {
this.resCodeMsg = codeMsg;
this.resData = null;
} /**
* 获取响应CodeMsg(外部WebApi专用)
*
* @param res
* @param <T>
* @return ServiceCodeMsgEnum.Success为正确
*/
private static <T> ServiceCodeMsgEnum getResponseCodeMsg(ServiceResponse<T> res) {
return res.getResCodeMsg();
} /**
* 获取响应Msg(内部站点专用)
*
* @param res
* @param <T>
* @return null为正确
*/
public static <T> String getResponseMsg(ServiceResponse<T> res) {
return Strings.emptyToNull(res.getResCodeMsg().getMsg());
} /**
* 获取响应参数
*
* @param res
* @param <T>
* @return
*/
public static <T> T getResponseData(ServiceResponse<T> res) {
return res.getResData();
}
}
3、创建统一响应结果枚举,这里可以统一控制响应CodeMsg的对应关系,使用也简单直观
/**
* 服务通信CodeMsg枚举
*/
public enum ServiceCodeMsgEnum { Success("000000", null),
Error("999999", "系统异常"); //region private String code;
private String msg; ServiceCodeMsgEnum(String code, String msg) {
this.code = code;
this.msg = msg;
} public String getCode() {
return code;
} public void setCode(String code) {
this.code = code;
} public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
} //endregion
}
4、服务生产者这边,统一入参和返回值都是ServiceRequest和ServiceResponse,又可以根据泛型来识别到底是什么对象
import com.google.gson.Gson;
import com.ysl.ts.common.serviceModel.ServiceCodeMsgEnum;
import com.ysl.ts.common.serviceModel.ServiceRequest;
import com.ysl.ts.common.serviceModel.ServiceResponse;
import com.ysl.ts.core.model.base.ts_base.SysUserModel;
import com.ysl.ts.core.service.base.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import java.util.List;
import java.util.Map; @Controller
@RequestMapping("/api/sysUser")
public class SysUserController extends BaseController {
@Autowired
SysUserService service; @ResponseBody
@RequestMapping("/save")
public ServiceResponse<Integer> save(@RequestBody ServiceRequest<SysUserModel> req) {
try {
//使用统一的方法获取请求Data
SysUserModel agent = ServiceRequest.getRequestData(req);
//调用Service
int result = service.save(agent);
//响应-成功
return new ServiceResponse<>(result);
} catch (Exception e) {
//响应-错误
return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
}
} @ResponseBody
@RequestMapping("/delete")
public ServiceResponse<Integer> delete(@RequestBody ServiceRequest<Integer> req) {
try {
//获取请求Data
int id = ServiceRequest.getRequestData(req);
//调用Service
int result = service.delete(id);
//响应-成功
return new ServiceResponse<>(result);
} catch (Exception e) {
//响应-错误
return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
}
} @ResponseBody
@RequestMapping("/get")
public ServiceResponse<SysUserModel> get(@RequestBody ServiceRequest<Integer> req) {
try {
//获取请求Data
int id = ServiceRequest.getRequestData(req);
//调用Service
SysUserModel result = service.get(id);
//响应-成功
return new ServiceResponse<>(result);
} catch (Exception e) {
//响应-错误
return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
}
} @ResponseBody
@RequestMapping("/list")
public ServiceResponse<List<SysUserModel>> list(@RequestBody ServiceRequest<String> req) {
try {
//获取请求Data
String search = ServiceRequest.getRequestData(req);
//调用Service
List<SysUserModel> result = service.list();
//响应-成功
return new ServiceResponse<>(result);
} catch (Exception e) {
//响应-错误
return new ServiceResponse<>(ServiceCodeMsgEnum.Error);
}
}
}
5、服务消费者这边,使用了Feign所以需要一个接口来实现调用,我们直接传入ServiceRequest<>来做统一的请求对象,返回ServiceResponse<>来做统一的响应对象
import com.ysl.ts.common.serviceModel.ServiceRequest;
import com.ysl.ts.common.serviceModel.ServiceResponse;
import com.ysl.ts.core.model.base.ts_base.SysUserModel;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; @Service
@FeignClient("YSL-TS-Core-Service-Base")//服务生产者名称
@RequestMapping("/api/sysUser")//服务路由
public interface SysUserService { @RequestMapping("/save")
ServiceResponse<Integer> save(@RequestBody ServiceRequest<SysUserModel> req); @RequestMapping("/delete")
ServiceResponse<Integer> delete(@RequestBody ServiceRequest<Integer> req); @RequestMapping("/get")
ServiceResponse<SysUserModel> get(@RequestBody ServiceRequest<Integer> req); @RequestMapping("/list")
ServiceResponse<List<SysUserModel>> list(@RequestBody ServiceRequest<String> req);
}
6、获得基础数据实体后,在页面展现之前可能有些字段需要进行翻译,比如状态1:启用,0:禁用等等,这部分建立一个ModelEx类继承Model类,把其中需要翻译的字段写在ModelEx中,
用以下转换类对实体值进行拷贝,然后页面接收这个ModelEx对象,这样默认可以使用父类中的属性,如果要显示翻译的就用子类中的属性即可
import java.util.ArrayList;
import java.util.List; /**
* Model 转换类
*
* @param <TModel> Model类型对象
* @param <TModelEx> ModelEx类型对象*/
public abstract class AbstractModelConvertor<TModel, TModelEx extends TModel> { /**
* 转换
*
* @param model Model类型对象
* @param modelEx ModelEx类型对象
* @return
*/
public TModelEx convert(TModel model, Class<TModelEx> modelEx) {
TModelEx ex = new DeepClone().clone(model, modelEx);
convertFields(ex);//填充翻译字段,需要子类重写
return ex;
} /**
* 列表转换
*
* @param modelList Model类型对象列表
* @param modelEx ModelEx类型对象
* @return
*/
public List<TModelEx> convert(List<TModel> modelList, Class<TModelEx> modelEx) {
List<TModelEx> list = new ArrayList<>(); for (TModel tModel : modelList) {
list.add(convert(tModel, modelEx));
} return list;
} /**
* 字段转换接口
*
* @param modelEx
*/
protected abstract void convertFields(TModelEx modelEx);
}
实际上就是使用Gson对实体做了一次序列化很简单
import com.google.gson.Gson; /**
* 深入拷贝
*/
public class DeepClone { private Gson gson = new Gson(); /**
* 深拷贝
*
* @param t 源数据
* @param clazz 目标类
* @param <T> 源数据类型
* @param <K> 目标类型
* @return
*/
public <T, K> K clone(T t, Class<K> clazz) {
return gson.fromJson(gson.toJson(t), clazz);
}
}
7、每一个实体都继承抽象基类,这样就可以直接使用转换方法了
import com.ysl.ts.common.AbstractModelConvertor;
import com.ysl.ts.core.model.base.ts_base.SysUserModel;
import com.ysl.ts.core.model.base.ts_base.ex.SysUserModelEx; public class SysUserConvertor extends AbstractModelConvertor<SysUserModel, SysUserModelEx> {
/**
* 填充待翻译字段
*/
@Override
protected void convertFields(SysUserModelEx sysUserModelEx) { }
}
8、下面就是页面Controller的Action调用了
import com.ysl.ts.common.serviceModel.ServiceRequest;
import com.ysl.ts.common.serviceModel.ServiceResponse;
import com.ysl.ts.core.model.base.ts_base.SysUserModel;
import com.ysl.ts.core.model.base.ts_base.ex.SysUserModelEx;
import com.ysl.ts.web.base.modelConvertors.ts_base.SysUserConvertor;
import com.ysl.ts.web.base.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList;
import java.util.List; @Controller
@Component("AdminSysUser")
@RequestMapping("/sysUser")
public class SysUserController extends BaseController {
//自动注入Feign接口对象
@Autowired
SysUserService service; @RequestMapping("/save")
public boolean save(SysUserModel agent) {
boolean result = false;
//创建ServiceRequest
ServiceRequest<SysUserModel> req = new ServiceRequest<>(agent);
//调用Service并获得ServiceResponse
ServiceResponse<Integer> res = service.save(req);
//解析获得Code
String msg = ServiceResponse.getResponseMsg(res);
//NULL代表响应正常
if (null == msg)
{
//解析获得T类型
result = ServiceResponse.getResponseData(res) > 0;
}
return result;
} @RequestMapping("/delete")
public boolean delete(int id){
boolean result = false;
//创建ServiceRequest
ServiceRequest<Integer> req = new ServiceRequest<>(id);
//调用Service并获得ServiceResponse
ServiceResponse<Integer> res = service.delete(req);
//解析获得Code
String msg = ServiceResponse.getResponseMsg(res);
//NULL代表响应正常
if (null == msg)
{
//解析获得T类型
result = ServiceResponse.getResponseData(res) > 0;
}
return result;
} @ResponseBody
@RequestMapping("/get")
public SysUserModelEx get(Model model, int id) {
SysUserModelEx result = new SysUserModelEx();
//创建ServiceRequest
ServiceRequest<Integer> req = new ServiceRequest<>(id);
//调用Service并获得ServiceResponse
ServiceResponse<SysUserModel> res = service.get(req);
//解析获得Code
String msg = ServiceResponse.getResponseMsg(res);
//NULL代表响应正常
if (null == msg)
{
//解析获得T类型
SysUserModel tmp = ServiceResponse.getResponseData(res);
//翻译所需字段
result = new SysUserConvertor().convert(tmp, SysUserModelEx.class);
}
return result;
//model.addAttribute("model", result);
//return "/hotel/get";
} //直接返回json不写页面了
@ResponseBody
@RequestMapping("/list")
public List<SysUserModelEx> list(String search) {
List<SysUserModelEx> result = new ArrayList<>();
//创建ServiceRequest
ServiceRequest<String> req = new ServiceRequest<>(search);
//调用Service并获得ServiceResponse
ServiceResponse<List<SysUserModel>> res = service.list(req);
//解析获得Code
String msg = ServiceResponse.getResponseMsg(res);
//NULL代表响应正常
if (null == msg)
{
//解析获得T类型
List<SysUserModel> tmp = ServiceResponse.getResponseData(res);
//翻译所需字段
result = new SysUserConvertor().convert(tmp, SysUserModelEx.class);
}
return result;
}
}
111
IDEA项目搭建十三——服务消费端与生产端通信实现的更多相关文章
- 为 rails 本地项目搭建 elasticsearch 服务
首先安装 elasticsearch 服务 OSX 系统 brew install elasticsearch brew services start elasticsearch 测试服务是否启动浏览 ...
- vue.js+koa2项目实战(四)搭建koa2服务端
搭建koa2服务端 安装两个版本的koa 一.版本安装 1.安装 koa1 npm install koa -g 注:必须安装到全局 2.安装 koa2 npm install koa@2 -g 二. ...
- Centos6.9 搭建rsync服务端与客户端 案例:全网备份项目
rsync的企业工作场景说明 1)定时备份 1.1生产场景集群架构服务器备份方案项目 借助cron+rsync把所有客户服务器数据同步到备份服务器 2)实时复制 本地数据传输模式(local-only ...
- SpringCloud系列三:SpringSecurity 安全访问(配置安全验证、服务消费端处理、无状态 Session 配置、定义公共安全配置程序类)
1.概念:SpringSecurity 安全访问 2.具体内容 所有的 Rest 服务最终都是暴露在公网上的,也就是说如果你的 Rest 服务属于一些你自己公司的私人业务,这样的结果会直接 导致你信息 ...
- Dubbo学习笔记4:服务消费端泛化调用与异步调用
本文借用dubbo.learn的Dubbo API方式来解释原理. 服务消费端泛化调用 前面我们讲解到,基于Spring和基于Dubbo API方式搭建简单的分布式系统时,服务消费端引入了一个SDK二 ...
- day97:MoFang:移动端APP开发准备&移动端项目搭建&APICloud前端框架
目录 1.移动端开发相关概念 1.APP类型 2.移动端屏幕介绍 3.移动端自适配方案 4.元信息(meta) 2.APP开发准备 1.注册APPCLoud账号 2.下载APP开发编辑器 3.下载AP ...
- .Net Core 3.1浏览器后端服务(一) Web API项目搭建
一.前言 基于CefSharp开发的浏览器项目已有一段时间,考虑到后期数据维护需要Server端来管理,故开启新篇章搭建浏览器后端服务.该项目前期以梳理服务端知识为主,后期将配合CefSharp浏览器 ...
- contos7搭建syslog服务端与客户端
搭建中心服务端1,编辑文件/etc/rsyslog.conf,找到以下内容,将前面的#注释符合去除#$ModLoad imtcp#$InputTCPServerRun 514 2,在/etc/rsys ...
- 《用OpenResty搭建高性能服务端》笔记
概要 <用OpenResty搭建高性能服务端>是OpenResty系列课程中的入门课程,主讲人:温铭老师.课程分为10个章节,侧重于OpenResty的基本概念和主要特点的介绍,包括它的指 ...
随机推荐
- 【详记MySql问题大全集】一、安装MySql
最近公司要从SqlServer转到MySql,期间在安装环境和数据迁移的过程中,遇到了一些不大不小问题,比如怎么重置密码.怎么设置大小写敏感等等. 这些问题都比较细比较杂,这边解决完可能过几天就忘了, ...
- Robot Framework - 1 - 测试用例与测试库
01- 关于测试库(Test libraries) Test libraries provide the actual testing capabilities to Robot Framework ...
- 【PHP篇】字符串基础
1.声明时既可以用双引号也可以用单引号 str1 =”字符串值”; //可解析引号里的变量等内容 str2=’字符串值’; //不可解析内容 2.字符串没有长度限制,但要注意内存的消耗 ...
- 浅谈HTTP Keep-Alive
背景是一次线上故障 项目类型vue ssr 与server的数据交互用的http内网域名方案 在5月发生了一次线上CPU100%的问题,直接导致了NodeServer 500. 最终解决办法是: 1. ...
- Express框架之Jade模板引擎使用
日期:2018-7-8 十月梦想 node.js 浏览:2952次 评论:0条 前段时间讲说了ejs模板引擎,提到了jade的效率等等问题!今天在这里简单提一下jade的使用方式!结合expr ...
- 《你不知道的JavaScript(上卷)》读书笔记
第一次尝试用思维导图记笔记,感觉还不错~~~不过还是改不了我读书笔记写成抄书笔记的毛病 =.= 因为开始学JS的时候,一般浏览器就已经支持ES6了,所以比较喜欢使用ES6语法,let,=>等,文 ...
- 两种方法:VS2008下C++窗体程序显示控制台的方法——在QT程序中使用cout和cin
老蔡写了一个基于QT的窗体程序,而过去写的类的调试信息都是用cout显示的,苦于窗体程序无法显示cout信息很多信息都看不到,于是就想到让控制台和窗体同时显示.显示控制台方法如下 1.项目(或者叫“工 ...
- [Shell]sed命令在MAC和Linux下的不同使用方式
---------------------------------------------------------------------------------------------------- ...
- Java并发编程之AQS
一.什么是AQS AQS(AbstractQueuedSynchronize:队列同步器)是用来构建锁或者其他同步组件的基础框架,很多同步类都是在它的基础上实现的,比如常用的ReentrantLock ...
- mysql 更新语句中加判断条件
UPDATE loan_overdue_list l setl.type_status=(CASE WHEN l.overdue_days>(select c.overdue_one from ...