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

posted @ 2016-03-07 19:52  青天流云  阅读(3280)  评论(0编辑  收藏  举报