HTTP+XML接口客户端 结合策略模式实现总结
在项目中,我们经常会使用到http+xml的接口,而且不仅仅的是一个,可能会有多个http的接口需要实时的交互.但是http接口的发送消息的公共部分是一样的,只有每个接口的报文解析和返回报文是不同的,此时考虑到把变化和不变化的隔离出来,采取用策略模式,把公共的部分代码抽取隔离出来,每个http接口的不同的处理逻辑单独自己处理,这样也方便了后期的修改和扩展,可以很方便的修改单独的接口处理逻辑和添加新的http接口到项目中使用,而不用修改以前的设计.下面就http+xml接口的发送客户端采用简单的策略模式实现:
项目背景:SSH架构
第一,实现发送客户端的基类,把接口需要的基本参数信息,抽取出来,封装公共的发送方法,封装公共的报文组合方法,以及注入不同接口消息处理的报
* 功能详细描述:HttpClient 发送报文基础类
*
* @author lilin
* @since 2015-1-7
*/
@Service
public class BaseHttpClientSender {
//注入接口的系统标示
@Value("${appCode}")
protected String appCode;
//注入权限id
@Value("${authId}")
protected String authId;
//注入需要访问服务方的地址url
@Value("${httpUrl}")
protected String url;
private Logger logger = Logger.getLogger(BaseHttpClientSender.class); private IHttpSenderHandle httpSenderHandle;
public void setHttpSenderHandle(IHttpSenderHandle httpSenderHandle) {
this.httpSenderHandle = httpSenderHandle;
} /**
* 功能描述: 发送接口
*
* @return 返回结果
* @since 2014-9-17
*/
public Map<String, Object> httpClentSender(final String msg) {
logger.info("@@HttpClient sendXml : " + msg);
HttpClient httpClient = new DefaultHttpClient();
Map<String, Object> result = new HashMap<String, Object>();
HttpPost method = new HttpPost(url);
ContentProducer cp = new ContentProducer() {
public void writeTo(OutputStream outstream) throws IOException {
Writer writer = new OutputStreamWriter(outstream, "UTF-8");
/**
* 获取请求的xml格式数据
*/
writer.write(msg);
writer.flush();
}
};
method.setEntity(new EntityTemplate(cp));
method.addHeader("Content-Type", "text/xml");
HttpResponse response = null;
try {
response = httpClient.execute(method);
} catch (ClientProtocolException e) {
logger.error("@@HttpClient Excute ERROR! ClientProtocolException:", e);
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "调用接口出错!");
} catch (IOException e) {
logger.error("@@HttpClient IOException!", e);
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "IO出错!");
}
if (response != null) {
int status = response.getStatusLine().getStatusCode();
logger.info("@@HttpClient statusCode : " + status);
if (status == HttpStatus.SC_OK) {
HttpEntity resEntity = response.getEntity();
try {
result = httpSenderHandle.handleResponseMsg(resEntity.getContent());
} catch (Exception e) {
logger.error("@@HttpClient Get ResponseBody ERROR!", e);
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "获取返回报文时出错!");
}
} else {
logger.info("@@HttpClient HttpStatus ERROR!");
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "接口返回失败!");
}
}
logger.info("@@HttpClient SUCCESS");
return result;
} /**
* 功能描述: 封装消息提醒头部
*
* @return mbfHeader
* @since 2014-9-18
* @version
*/
protected Element getMbfHeader(String serviceCode, String opertion) {
Element mbfHeader = DocumentHelper.createElement("MbfHeader");
addElementHead(mbfHeader, "ServiceCode", serviceCode);
addElementHead(mbfHeader, "Operation", opertion);
addElementHead(mbfHeader, "AppCode", appCode);
addElementHead(mbfHeader, "UId", UUID.randomUUID().toString());
addElementHead(mbfHeader, "AuthId", authId);
return mbfHeader;
} /**
* 在目标节点上面增加一个节点: <br>
* 〈在目标节点上面增加一个节点,并返回增加的节点,节点的内容根据传入的elementText定〉<br>
* 如果传入的文本是null,那么仅仅增加节点,不增加value
*
* @param targetElement
* @param elementName
* @param elementText
* @return
*/
protected Element addElement(Element targetElement, String elementName, String elementText) {
Element temp = targetElement.addElement(elementName);
if (elementText != null) {
temp.addCDATA(elementText);
}
return temp;
} /**
* 在目标节点上面增加一个节点: <br>
* 〈在目标节点上面增加一个节点,并返回增加的节点,节点的内容根据传入的elementText定〉<br>
* 如果传入的文本是null,那么仅仅增加节点,不增加value
*
* @param targetElement
* @param elementName
* @param elementText
* @return
*/
protected Element addElementHead(Element targetElement, String elementName, String elementText) {
Element temp = targetElement.addElement(elementName);
if (elementText != null) {
temp.addText(elementText);
}
return temp;
} public String getAppCode() {
return appCode;
} public void setAppCode(String appCode) {
this.appCode = appCode;
} public String getAuthId() {
return authId;
} public void setAuthId(String authId) {
this.authId = authId;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} }
第二,实现业务类需要注入的实际发送接口信息的子类<LowPriceApproveHttpSender> 继承自基类实现,同时注入自己需要的基本参数信息,和实际的消息处理接口实现类.
/**
* 功能详细描述:超低价审批的接口实际发送
*
* @author lilin
* @since 2014-9-17
*/
@Service
public class LowPriceApproveHttpSender extends BaseHttpClientSender { private Logger logger = Logger.getLogger(LowPriceApproveHttpSender.class); @Value("ZYCRMExamineResults")
private String serviceCode;
@Value("examineResults")
private String operation; @Resource
private IHttpSenderHandle lowPriceApproveHttpSenderHandle; @PostConstruct
public void injectHttpSenderHandle() {
super.setHttpSenderHandle(lowPriceApproveHttpSenderHandle);
} public Map<String, Object> sendCrm(PriceBaseInfo base, LowPriceApprove approve, List<LowPriceDetail> details,
User user, String nextStep) {
logger.info("LowPrice APPROVE sendCrm START");
// 获取报文
String msg = getSenderMsg(base, approve, details, user, nextStep);
// 发送
Map<String, Object> res = super.httpClentSender(msg);
return res;
} private String getSenderMsg(PriceBaseInfo base, LowPriceApprove approve, List<LowPriceDetail> details, User user,
String nextStep) { Document document = DocumentHelper.createDocument();
document.setXMLEncoding("UTF-8");
Element root = document.addElement("MbfService");
Element input1 = root.addElement("input1");
input1.add(getMbfHeader(serviceCode, operation)); Element mbfBody = root.addElement("MbfBody");
Element input = mbfBody.addElement("input"); addElement(input, "applNo", base.getBusinessCode());
addElement(input, "apprDate", base.getEndDate().split(" ")[0]); // 如果为超公司底价审批
if ("cmp".equals(approve.getBranchType())) {
String reportUnitPrice = approve.getReportUnitPrice();
addElement(input, "apprPrice", StringUtils.isEmpty(reportUnitPrice) ? approve.getSignUnitPrice()
: reportUnitPrice);
addElement(input, "apprName", StringUtils.isNotEmpty(approve.getApprovalName()) ? approve.getApprovalName()
: user.getUserName());
addElement(input, "operatorNo", user.getUserId());
} else {
addElement(input, "apprPrice", null);
addElement(input, "apprName", null);
addElement(input, "operatorNo", null);
}
addElement(input, "apprTime", base.getEndDate().split(" ")[1]);
addElement(input, "crmNo", approve.getOrderNo());
addElement(input, "personId", base.getApplierNo());
addElement(input, "projCode", approve.getProjectCode());
addElement(input, "result", "SE".equals(nextStep) ? "1" : "2"); if (CollectionUtils.isNotEmpty(details)) {
Element tables = mbfBody.addElement("tables");
for (LowPriceDetail detail : details) {
Element tQuota = tables.addElement("tQuota");
addElement(tQuota, "limitType", detail.getLimitType());
addElement(tQuota, "useValue", detail.getAssignLimit());
addElement(tQuota, "apprRemark", detail.getRemark());
}
}
return document.asXML();
}
}
第三,定义好消息处理接口类:所有的接口处理实际类 统一实现此接口,接口用于发送消息基类的注入.实际的处理类在子类中注入实现.
/**
* 发送Http接口
* @author lilin
* @since 20150918
*/
public interface IHttpSenderHandle { /**
*
* 功能描述: <br>
* 〈功能详细描述〉
*
* @param content
* @return
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
Map<String, Object> handleResponseMsg(InputStream content); }
第四,实现,每个接口需要实际的处理消息的类:用于消息发送子类的组合注入
/**
* 功能详细描述: httpClient 接口返回消息
*
* @author lilin
* @since 2014-9-17
*/
@Service
public class LowPriceApproveHttpSenderHandle implements IHttpSenderHandle { private Logger logger = Logger.getLogger(LowPriceApproveHttpSenderHandle.class); @Override
public Map<String, Object> handleResponseMsg(InputStream inputStream) { Map<String, Object> result = new HashMap<String, Object>();
SAXReader sb = new SAXReader();
Document document;
try {
document = sb.read(inputStream);
} catch (DocumentException e) {
logger.info("ERROR IN Reader InputStream:", e);
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "返回报文转换出错!");
return result;
}
logger.info("@@HttpClient 解析返回报文:" + document.asXML());
Element root = document.getRootElement();
Element outElement = root.element("output1");
Element mbfHeader = outElement.element("MbfHeader");
Element serviceResponse = mbfHeader.element("ServiceResponse");
Element status = serviceResponse.element("Status");
if ("COMPLETE".equals(status.getText())) {
Element bodyElement = outElement.element("MbfBody");
Element output = bodyElement.element("output");
Element reFlag = output.element("reFlag");
Element errorMessage = output.element("errorMessage"); result.put(Constants.RESFLAG, reFlag.getText());
result.put(Constants.RESMSG, errorMessage.getText());
} else {
logger.info("@@HttpClient 接口没有成功返回:" + status.getText());
} return result;
} }
到此,简单的http+xml+策略模式的实现消息的发送客户端就完成了,此时,只要在我们需要调用的服务类之中,注入我们的客户端发送子类bean,就能实时的发送xml消息交互了.
后面扩展和修改也十分的方便,不需要修改已有的设计和代码:
新增一个新的发送接口:
1,新增加发送子类,实现当前的发送基类<BaseHttpClientSender>,注入需要处理消息的方法handle类.
2,新增处理消息的handle类,实现当前的<IHttpSenderHandle>接口,
3,把新增加的子类发送类的bean,注入到需要调用发送接口的服务类中,就可以方便的实现接口信息的报文发送请求了.
HTTP+XML接口客户端 结合策略模式实现总结的更多相关文章
- 用Map+函数式接口来实现策略模式
用Map+函数式接口来实现策略模式 目前在魔都,贝壳找房是我的雇主,平时关注一些 java 领域相关的技术,希望你们能在这篇文章中找到些有用的东西.个人水平有限,如果文章有错误还请指出,在留言区一起交 ...
- 为什么会有Comparable与Comparator接口? 引入策略模式
目录 引入 Comparable接口的来龙去脉 引入Comparator接口 什么是策略模式? 使用了策略模式有什么好处? 引入 大家先考虑一个场景, 有一个整形数组, 我们希望通过调用一个工具类的排 ...
- C++设计模式-策略模式(2)
策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中从而使得它们可以相互替换. 策略模式使得算法可以在不影响到客户端的情况下发生变化.策略模把行为和环境分开.环境类负责维持和查询 ...
- [工作中的设计模式]策略模式stategy
一.模式解析 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 策略模式的关键点为: 1.多种算法存在 2.算法继承同样的接口 ...
- C++设计模式——策略模式
策略模式 在GOF的<设计模式:可复用面向对象软件的基础>一书中对策略模式是这样说的:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.该模式使得算法可独立于使用它的客户而变化 ...
- 设计模式原来如此-策略模式(Strategy Pattern)
策略模式中体现了两个非常基本的面向对象设计的原则:1.封装变化的概念.2.编程中使用接口,而不是对接口的实现. 策略模式的定义:定义一组算法,将每个算法都封装起来,并使它们之间可以互换.策略模式使这些 ...
- ASP.NET MVC 学习笔记-2.Razor语法 ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础 反射的具体应用 策略模式的具体应用 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用 C#读取XML文件的基类实现
ASP.NET MVC 学习笔记-2.Razor语法 1. 表达式 表达式必须跟在“@”符号之后, 2. 代码块 代码块必须位于“@{}”中,并且每行代码必须以“: ...
- http接口服务方结合策略模式实现总结
在项目中,我们经常会使用到http+xml的接口,而且不仅仅的是一个,可能会有多个http的接口需要实时的交互.但是http接口的接收消息的公共部分是一样的,只有每个接口的报文解析和返回报文是不同的, ...
- C#委托和事件?策略模式?接口回调?还不清楚的赶紧来看我扯
早前学习委托的时候,写过一点东西,今天带着新的思考和认知,再记点东西.这篇文章扯到设计模式中的策略模式,观察者模式,还有.NET的特性之一--委托.真的,请相信我,我只是在扯淡...... 场景练习 ...
随机推荐
- YoC云上芯片家族迎来新成员
Espressif 乐鑫信息科技(以下简称乐鑫科技)近日在上海召开发布会,发布其旗下最新的旗舰同时也是第二代Yun on Chip(简称YoC)云上芯片ESP32.YoC云上芯片是由YunOS牵头,联 ...
- 微信App支付:微信支付的appid,appsecret,商户号mch_id,微信交易支付密钥(mch_key)在哪里查看
1-1) 查看微信支付 appid 的方法 微信支付使用的 appid, 是微信服务号的 appid, 需要你登录微信服务号后台, 在 开发-基本配置/开发者ID(AppID) 中查看微信支付 app ...
- 人工智能 Python 入门视频
Python, 是一种面向对象.解释型计算机程序设计语言,由Guido van Rossum于1989年发明,第一个公开发行版发行于1991年. Python是纯粹的自由软件, 源代码和解 ...
- ListUtil常用操作
/** * 获取列表总页数 */ public static <T> int getListPages(List<T> list,int pageNum,int pageSiz ...
- vue中使用base64进行加解密
vue进行Base64加解密 背景 项目中需要对特殊字符进行处理,避免json和数据库的特殊字符(""等)冲突,刚好学了信息安全,干脆整个加解密,wkk.. 使用步骤 打开dos, ...
- luogu P4756 Added Sequence(凸包+思维)
一眼望去不会. 考虑问题中的\(f(i,j)=|\sum_{p=i}^{j}a_p |\)的实际意义. 其实就是前缀和相减的绝对值. \(f(i,j)=|\ sum[j]-sum[i-1]\ |\ ...
- P2024 食物链 (补集)
题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我 ...
- python 协程 greenlet gevent
一.并发的本质 切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作系统强制控制),一种情况是该任务发生了阻塞,另外一种情况是该任务计算的时间过长时间片到了 二.协程 ...
- python第三周:集合、函数、编码、文件
1.集合: 集合的创建: list_1 = set([1,2,3,4,5]) list_2 = set([2,3,44,7,8]) 集合的特性:集合是无序的,集合可以去掉重复的元素 集合的操作:求交集 ...
- OA项目总结3
struts2自定义标签中 使用in 判断当前值 是否在某个集合中 该属性一方面可以获取前端页面传递过来的参数 另外一个作用就是在数据回显时把用户已经拥有的权限id存入该集合中 放在栈顶 ...