一、简介

之前已经完成了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项目搭建十三——服务消费端与生产端通信实现的更多相关文章

  1. 为 rails 本地项目搭建 elasticsearch 服务

    首先安装 elasticsearch 服务 OSX 系统 brew install elasticsearch brew services start elasticsearch 测试服务是否启动浏览 ...

  2. vue.js+koa2项目实战(四)搭建koa2服务端

    搭建koa2服务端 安装两个版本的koa 一.版本安装 1.安装 koa1 npm install koa -g 注:必须安装到全局 2.安装 koa2 npm install koa@2 -g 二. ...

  3. Centos6.9 搭建rsync服务端与客户端 案例:全网备份项目

    rsync的企业工作场景说明 1)定时备份 1.1生产场景集群架构服务器备份方案项目 借助cron+rsync把所有客户服务器数据同步到备份服务器 2)实时复制 本地数据传输模式(local-only ...

  4. SpringCloud系列三:SpringSecurity 安全访问(配置安全验证、服务消费端处理、无状态 Session 配置、定义公共安全配置程序类)

    1.概念:SpringSecurity 安全访问 2.具体内容 所有的 Rest 服务最终都是暴露在公网上的,也就是说如果你的 Rest 服务属于一些你自己公司的私人业务,这样的结果会直接 导致你信息 ...

  5. Dubbo学习笔记4:服务消费端泛化调用与异步调用

    本文借用dubbo.learn的Dubbo API方式来解释原理. 服务消费端泛化调用 前面我们讲解到,基于Spring和基于Dubbo API方式搭建简单的分布式系统时,服务消费端引入了一个SDK二 ...

  6. day97:MoFang:移动端APP开发准备&移动端项目搭建&APICloud前端框架

    目录 1.移动端开发相关概念 1.APP类型 2.移动端屏幕介绍 3.移动端自适配方案 4.元信息(meta) 2.APP开发准备 1.注册APPCLoud账号 2.下载APP开发编辑器 3.下载AP ...

  7. .Net Core 3.1浏览器后端服务(一) Web API项目搭建

    一.前言 基于CefSharp开发的浏览器项目已有一段时间,考虑到后期数据维护需要Server端来管理,故开启新篇章搭建浏览器后端服务.该项目前期以梳理服务端知识为主,后期将配合CefSharp浏览器 ...

  8. contos7搭建syslog服务端与客户端

    搭建中心服务端1,编辑文件/etc/rsyslog.conf,找到以下内容,将前面的#注释符合去除#$ModLoad imtcp#$InputTCPServerRun 514 2,在/etc/rsys ...

  9. 《用OpenResty搭建高性能服务端》笔记

    概要 <用OpenResty搭建高性能服务端>是OpenResty系列课程中的入门课程,主讲人:温铭老师.课程分为10个章节,侧重于OpenResty的基本概念和主要特点的介绍,包括它的指 ...

随机推荐

  1. 【详记MySql问题大全集】四、设置MySql大小写敏感(踩坑血泪史)

    系列目录 一.安装MySql 二.安装并破解Navicat 三.没有my.in配置文件怎么办 四.设置MySql的大小写敏感 五.重置MySql登陆密码 这一篇可以说是我的踩坑的血泪史了... MyS ...

  2. Kibana简单使用教程

    ELK平台日志查看教程 1. 访问地址:http://xxx:5601/app/kibana 我们主要使用的是右边Discover功能,默认显示的是183tpp(可设置)最近15分钟日志信息. 2. ...

  3. sql server I/O硬盘交互

    一. 概述 sql server作为关系型数据库,需要进行数据存储, 那在运行中就会不断的与硬盘进行读写交互.如果读写不能正确快速的完成,就会出现性能问题以及数据库损坏问题.下面讲讲引起I/O的产生, ...

  4. restTemplate.postForObject上传文件中文乱码(???.xls)

    一.问题描述 项目中, 使用restTemplate上传文件时, 文件名中文乱码, 一串问号, 源文件名为: 测试中文乱码哦哦哦.zip, 通过restTemplate.postForObject调用 ...

  5. 一个前端开发者换电脑的过程(IDE篇)

    一台全新的电脑,需要做出怎样的改变,才可以摇身一变成为前端开发者能用的电脑呢.首先,我们需要安装一个编辑器,这里我们选择目前最火的vscode. 先去到它的官网,把对应版本的vscode下载下来. 然 ...

  6. struts转发和重定向action

    1.转发(服务器端跳转) <action name="rederTo"> <result type="chain">hello</ ...

  7. web认证

    前言: HTTP是无状态协议,之前已认证成功的用户状态无法通过协议层面保存下来.即无法实现状态管理,我们使用Cookie来管理Session(会话),以弥补HTTP协议中不存在的状态管理功能. 步骤: ...

  8. 高性能Mysql笔记 — explain

    explain 查看sql的执行计划,只是一个近似结果,一般不会实际执行该sql,如果有子查询就会执行子查询 explain table_name,这儿的table_name含义较广:子查询.unio ...

  9. spring boot多数据源配置(mysql,redis,mongodb)实战

    使用Spring Boot Starter提升效率 虽然不同的starter实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties和AutoConfi ...

  10. EF(EntityFramework)与mysql使用,取数据报错,linq实体映射错误

    报错信息:LINQ to Entities does not recognize the method 'System.String ToString()' method, and this meth ...