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

  • 相关阅读:
    分形之城:递归超典型例题,还没明白?手把手画给你看!
    如何优雅地实现浏览器兼容与CSS规则回退
    巧技拾遗 | JavaScript 中 Array.every 和 Array.map 的巧妙结合
    又被分治题卡住好几个小时!用最笨的方法搞懂分治法边界,告别死循环!
    【吐血经验】在 windows 上安装 spark 遇到的一些坑 | 避坑指南
    CCERT月报:Struts2漏洞已成高校网络安全顽疾(转)
    聪明的程序员会绕开这些雷(转)
    我为什么支持高校的信息系统放弃Apache Struts2框架(五)
    我为什么支持高校的信息系统放弃Apache Struts2框架(四)
    我为什么支持高校的信息系统放弃Apache Struts2框架(三)
  • 原文地址:https://www.cnblogs.com/lilin0719/p/5251680.html
Copyright © 2011-2022 走看看