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,注入到需要调用发送接口的服务类中,就可以方便的实现接口信息的报文发送请求了.