新增webservice呼叫日誌

Gin.p發表於2016-09-16

  之前想用spring的AOP給webservice新增切面的,但是使用around切面後,居然呼叫端得不到webservice的返回結果,而且報文的詳細情況也不得而知,很是尷尬,所以偷了個懶。但是該做的還是要做,不要以後要求檢視呼叫日誌的時候,什麼都拿不出,不是一個尷尬能搞定的。

  我使用的是基於cxf的webservice,所以新增呼叫日誌的方法也是基於cxf的,其次是配合sping開發webservice。基本的webservice的實現,這裡就不再說明。

 一、使用LoggingInInterceptor實現

  第一種方法,就是使用已經的實現的日誌攔截,需要配置好log.properties。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd ">

    <jaxws:endpoint implementor="com.test.DepartServiceImpl" address="/departService">
        <jaxws:inInterceptors>
            <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
        </jaxws:inInterceptors>
        <jaxws:outInterceptors>
            <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
        </jaxws:outInterceptors>
    </jaxws:endpoint>
    
</beans>
  <jaxws:inInterceptors>是輸入攔截器,在介面被呼叫的時候會被攔截。下面是介面被呼叫後的日誌輸出。
----------------------------
ID: 1
Address: http://localhost:8080/test/ws/departService?wsdl
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], Content-Length=[221], content-type=[text/xml; charset=UTF-8], host=[localhost:18080], pragma=[no-cache], SOAPAction=[""], user-agent=[Apache CXF 2.7.7]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:findEmployees xmlns:ns2="http://cxf.test.com/"><arg0>張三</arg0><arg1></arg1></ns2:findEmployees></soap:Body></soap:Envelope>
--------------------------------------
  <jaxws:outInterceptors>是輸出攔截器,在介面呼叫完畢的時候被攔截。下面是介面呼叫完成後的日誌輸出。
---------------------------
ID: 1
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:findEmployeesResponse xmlns:ns2="http://cxf.test.com/"><return><user_depart>IT</user_depart><user_name>張三</user_name><user_state>在職</user_state><user_tele></user_tele><user_type>普通員工</user_type></return></ns2:findEmployeesResponse></soap:Body></soap:Envelope>
--------------------------------------

  我們可以看到基本的實現已經非常詳細,甚至能看到一些重要基本的資訊。如果這個方法還不能滿足你的需求,那麼請看後面。

 二、實現AbstractPhaseInterceptor

import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;


public class WebserviceLogInterceptor extends AbstractPhaseInterceptor<Message> {

    public WebserviceLogInterceptor(){
        super(Phase.RECEIVE);
    }

    @Override
    public void handleMessage(Message message) throws Fault {
        StringBuffer sb = new StringBuffer();
        //這裡可以對流做處理,從流中讀取資料,然後修改為自己想要的資料
        InputStream is = message.getContent(InputStream.class);
        String encoding = (String)message.get(Message.ENCODING);
        int len = 0;
        byte[] bytes = new byte[1024 * 4];
        try {
            while((len = is.read(bytes))!=-1){
                sb.append(new String(bytes, 0, len, encoding));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(sb.toString());
      //在這裡需要注意一點的是,如果修改後的soap訊息格式不符合webservice框架格式,比如:框架封裝後的格式為 //<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" <soap:Body> //<這裡是呼叫服務端方法的名稱空間><這是引數名稱> //這裡才是真正的訊息 //</這裡是呼叫服務端方法的名稱空間></這是引數名稱> //</soap:Body> //</soap:Envelope> try { message.setContent(InputStream.class, new ByteArrayInputStream(sb.toString().getBytes(encoding))); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } }

  配置檔案新增對應的攔截器。

<jaxws:endpoint implementor="com.test.cxf.impl.DepartServiceImpl" address="/departService">
        <jaxws:inInterceptors>
            <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
            <bean class="com.test.interceptor.WebserviceLogInterceptor" />
        </jaxws:inInterceptors>
    </jaxws:endpoint>

  需要注意的是,在處理完資料後,需要在寫回到流中,不然介面無法的到引數。

 

import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;

import java.io.*;

public class WebserviceLogInterceptor1 extends AbstractPhaseInterceptor<Message> {

    public WebserviceLogInterceptor1(){
        super(Phase.PRE_STREAM);
    }

    @Override
    public void handleMessage(Message message) throws Fault {
        StringBuffer sb = new StringBuffer();
        OutputStream os = message.getContent(OutputStream.class);

        message.setContent(OutputStream.class, new CachedOutputStream());

        message.getInterceptorChain().doIntercept(message);

        CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
        try {
            String encoding = (String)message.get(Message.ENCODING);
            InputStream in = csnew.getInputStream();

            int len = 0;
            byte[] bytes = new byte[1024 * 4];
            while((len = in.read(bytes))!=-1) {
                sb.append(new String(bytes, 0, len, encoding));
            }
            System.out.println(sb.toString());
            //這裡對xml做處理,處理完後同理,寫回流中
            IOUtils.copy(new ByteArrayInputStream(sb.toString().getBytes(encoding)), os);

            os.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }

        message.setContent(OutputStream.class, os);
    }
}

  同樣的,在讀取完資料後,仍需要寫回到流中,不然呼叫端得不到結果。

相關文章