JAVA RESTful WebService實(shí)戰(zhàn)筆記(三)

前言

AOP(Aspect oriented Programming,面向切面編程),其實(shí)現(xiàn)原理就是代理被調(diào)用的方法,在其被執(zhí)行的方法前后,增加額外的業(yè)務(wù)功能,AOP的實(shí)現(xiàn)機(jī)制就是通過注解或者XML配置,就這些配置,動(dòng)態(tài)的生成字節(jié)碼(bytecode).使被調(diào)用代碼對(duì)應(yīng)的字節(jié)碼被環(huán)繞注入新的功能;或者使用Java的動(dòng)態(tài)代理機(jī)制,完成對(duì)被調(diào)用方法的增強(qiáng).

REST請(qǐng)求流程

在以下的圖中,請(qǐng)求流程中存在3種角色,分別是用戶、REST客戶端和REST服務(wù)器。請(qǐng)求始于請(qǐng)求的發(fā)送,止于調(diào)用Response類的readeEntity()方法,獲取響應(yīng)實(shí)體。

image.png
  • 1、用戶提交請(qǐng)求數(shù)據(jù),客戶端接收請(qǐng)求,進(jìn)入第一個(gè)擴(kuò)展點(diǎn):"客戶端請(qǐng)求過濾器ClientRequestFilter實(shí)現(xiàn)類"的filter()方法。
  • 2、請(qǐng)求過濾器處理完畢之后,流程進(jìn)入第二個(gè)擴(kuò)展點(diǎn):"客戶端寫攔截器WriterInterceptor實(shí)現(xiàn)類"的aroundWriteTo()方法,實(shí)現(xiàn)對(duì)客戶端序列化操作的攔截
  • 3、"客戶端消息體寫處理器MessageBodyWriter"執(zhí)行序列化,流程從客戶端過渡到服務(wù)器端
  • 4、服務(wù)器接收請(qǐng)求,流程進(jìn)入第三個(gè)擴(kuò)展點(diǎn):"服務(wù)器前置請(qǐng)求過濾器ContainerRequestFilter實(shí)現(xiàn)類"的filter()方法。
  • 5、過濾處理完畢后,服務(wù)器根據(jù)請(qǐng)求匹配資源方法,如果匹配到相應(yīng)的資源方法,流程進(jìn)入第四個(gè)擴(kuò)展點(diǎn):"服務(wù)器后置請(qǐng)求過濾器ContainerRequestFilter實(shí)現(xiàn)類"的filter()方法
  • 6、后置請(qǐng)求過濾器處理完畢之后,流程進(jìn)入第五個(gè)擴(kuò)展點(diǎn):"服務(wù)器讀攔截器ReaderInterceptor實(shí)現(xiàn)類的"aroundReadFrom()方法,攔截服務(wù)器端反序列化操作
  • 7、"服務(wù)器消息體讀處理器MessageBodyReader"完成對(duì)客戶端數(shù)據(jù)流的反序列化。服務(wù)器執(zhí)行匹配的資源方法
  • 8、REST請(qǐng)求資源處理完畢之后,流程進(jìn)入第六個(gè)擴(kuò)展點(diǎn):"服務(wù)器相應(yīng)過濾器ContainerResponseFilter實(shí)現(xiàn)類"的filter()方法
  • 9、過濾器處理完畢之后,流程進(jìn)入第七個(gè)擴(kuò)展點(diǎn):"服務(wù)器寫攔截器WriterInterceptor實(shí)現(xiàn)類"的aroundWriteTo()方法,對(duì)服務(wù)器端序列化到客戶端這個(gè)操作的攔截
  • 10、"服務(wù)器消息體寫處理器MessageBodyWriter"執(zhí)行序列化,流程返回到客戶端一側(cè)。
  • 11、客戶端接收響應(yīng),流程進(jìn)入第八個(gè)擴(kuò)展點(diǎn):"客戶端響應(yīng)過濾器ClientResponseFilter實(shí)現(xiàn)類"的filter()方法
  • 12、過濾器處理完畢后,客戶端響應(yīng)實(shí)例response返回到用戶一側(cè),用戶執(zhí)行response.readEntity()流程進(jìn)入第九個(gè)擴(kuò)展點(diǎn):"客戶端讀取攔截器ReaderInterceptor實(shí)現(xiàn)類"的aroundReadFrom()方法,對(duì)客戶端反序列化進(jìn)行攔截
  • 13、"客戶端消息體賭徒處理器MessageBodyReader"執(zhí)行反序列化,將Java類型的對(duì)象最終作為readEntity()方法的返回值。到此,一次REST請(qǐng)求處理器的完整流程完畢

這期間如果出現(xiàn)異?;蛘哔Y源不匹配情況,會(huì)從出錯(cuò)點(diǎn)開始結(jié)束流程

REST過濾器

1、ClientRequestFilter
  • 客戶端請(qǐng)求過濾器(ClientRequestFilter)定義的過濾方法filter()包含一個(gè)輸入?yún)?shù),是客戶端請(qǐng)求的上下文類ClientRequestFilter。從該上下文中可以獲取請(qǐng)求信息,典型的示例包括獲取請(qǐng)求context.getMethod(),獲取請(qǐng)求資源地址context.getUri()和獲取請(qǐng)求頭信息context.getHeaders()等。過濾器的實(shí)現(xiàn)類中可以利用這些信息,覆寫改方法以實(shí)現(xiàn)特有的過濾功能。ClientRequestFilter接口的實(shí)現(xiàn)類如下:
image.png
2、ContainerRequestFilter
  • 針對(duì)過濾切面,服務(wù)器請(qǐng)求過濾接口ContainerRequestFilter的實(shí)現(xiàn)類可以定義為預(yù)處理和后處理,默認(rèn)情況下,采用后處理方式。及先執(zhí)行容器接收請(qǐng)求操作.當(dāng)服務(wù)器接收并處理請(qǐng)求后.流程才進(jìn)入過濾器實(shí)現(xiàn)類的filter()方法。而預(yù)處理是在服務(wù)器處理接收到的請(qǐng)求之前就執(zhí)行過濾。如果希望實(shí)現(xiàn)一個(gè)預(yù)處理的過濾器實(shí)現(xiàn)類,需要在類名上定義注解@PreMatching

  • 服務(wù)器請(qǐng)求過濾器定義的過濾方法filter()包含一個(gè)輸入?yún)?shù),即容器請(qǐng)求上下文類ContainerRequestContext。ContainerRequestFilter接口的實(shí)現(xiàn)類如下:


    image.png
  • 如下代碼展示了ContainerRequestFilter接口的實(shí)現(xiàn)類,我們以CsrfProtectionFilter為例來說明,示例代碼如下所示:

public class CsrfProtectionFilter implements ClientRequestFilter {

    /**
     * Name of the header this filter will attach to the request.
     */
    public static final String HEADER_NAME = "X-Requested-By";

    private static final Set<String> METHODS_TO_IGNORE;

    static {
        HashSet<String> mti = new HashSet<String>();
        mti.add("GET");
        mti.add("OPTIONS");
        mti.add("HEAD");
        METHODS_TO_IGNORE = Collections.unmodifiableSet(mti);
    }

    private final String requestedBy;

    /**
     * Creates a new instance of the filter with X-Requested-By header value set to empty string.
     */
    public CsrfProtectionFilter() {
        this("");
    }

    /**
     * Initialized the filter with a desired value of the X-Requested-By header.
     *
     * @param requestedBy Desired value of X-Requested-By header the filter
     *                    will be adding for all potentially state changing requests.
     */
    public CsrfProtectionFilter(final String requestedBy) {
        this.requestedBy = requestedBy;
    }

    @Override
    public void filter(ClientRequestContext rc) throws IOException {
        if (!METHODS_TO_IGNORE.contains(rc.getMethod()) && !rc.getHeaders().containsKey(HEADER_NAME)) {
            rc.getHeaders().add(HEADER_NAME, requestedBy);
        }
    }
}

上述代碼中,CsrfProtectionFilter定義了一個(gè)特殊的頭信息"X-Requested-By"和CSRF忽略監(jiān)控的方法集合。在過濾器的filter()方法中,首先從上下文中獲取頭信息rc.getHeaders()和請(qǐng)求方法信息rc.getMethod(),然后判斷頭信息是否包含“X-Requested-By”,方法信息是否是安全的請(qǐng)求方法,即"GET"、"OPTIONS"或"HEAD",如果兩個(gè)條件不成立,過濾器會(huì)拋出一個(gè)運(yùn)行時(shí)異常BadRequestException

3、ContainerResponseFilter
  • 服務(wù)器響應(yīng)過濾器接口ContainerResponseFilter定義的過濾方法filter()包含兩個(gè)輸入?yún)?shù),一個(gè)是容器請(qǐng)求上下文類ContainerRequestContext,另一個(gè)是容器響應(yīng)上下文類ContainerResponseContext。ContainerResponseFilter接口的實(shí)現(xiàn)類如下圖所示:
image.png

我們通過EncodingFilter為例來說明。

 @Override
    public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
        if (!response.hasEntity()) {
            return;
        }

        // add Accept-Encoding to Vary header
        List<String> varyHeader = response.getStringHeaders().get(HttpHeaders.VARY);
        if (varyHeader == null || !varyHeader.contains(HttpHeaders.ACCEPT_ENCODING)) {
            response.getHeaders().add(HttpHeaders.VARY, HttpHeaders.ACCEPT_ENCODING);
        }

        // if Content-Encoding is already set, don't do anything
        if (response.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING) != null) {
            return;
        }

        // retrieve the list of accepted encodings
        List<String> acceptEncoding = request.getHeaders().get(HttpHeaders.ACCEPT_ENCODING);

        // if empty, don't do anything
        if (acceptEncoding == null || acceptEncoding.isEmpty()) {
            return;
        }

        // convert encodings from String to Encoding objects
        List<ContentEncoding> encodings = Lists.newArrayList();
        for (String input : acceptEncoding) {
            String[] tokens = input.split(",");
            for (String token : tokens) {
                try {
                    ContentEncoding encoding = ContentEncoding.fromString(token);
                    encodings.add(encoding);
                } catch (ParseException e) {
                    // ignore the encoding that could not parse
                    // but log the exception
                    Logger.getLogger(EncodingFilter.class.getName()).log(Level.WARNING, e.getLocalizedMessage(), e);
                }
            }
        }
        // sort based on quality parameter
        Collections.sort(encodings);
        // make sure IDENTITY_ENCODING is at the end (since it accepted if not explicitly forbidden
        // in the Accept-Content header by assigning q=0
        encodings.add(new ContentEncoding(IDENTITY_ENCODING, -1));

        // get a copy of supported encoding (we'll be modifying this set, hence the copy)
        SortedSet<String> acceptedEncodings = Sets.newTreeSet(getSupportedEncodings());

        // indicates that we can pick any of the encodings that remained in the acceptedEncodings set
        boolean anyRemaining = false;
        // final resulting value of the Content-Encoding header to be set
        String contentEncoding = null;

        // iterate through the accepted encodings, starting with the highest quality one
        for (ContentEncoding encoding : encodings) {
            if (encoding.q == 0) {
                // ok, we are down at 0 quality
                if ("*".equals(encoding.name)) {
                    // no other encoding is acceptable
                    break;
                }
                // all the 0 quality encodings need to be removed from the accepted ones (these are explicitly
                // forbidden by the client)
                acceptedEncodings.remove(encoding.name);
            } else {
                if ("*".equals(encoding.name)) {
                    // any remaining encoding (after filtering out q=0) will be acceptable
                    anyRemaining = true;
                } else {
                    if (acceptedEncodings.contains(encoding.name)) {
                        // found an acceptable one -> we are done
                        contentEncoding = encoding.name;
                        break;
                    }
                }
            }
        }

        if (contentEncoding == null) {
            // haven't found any explicit acceptable encoding, let's see if we can just pick any of the remaining ones
            // (if there are any left)
            if (anyRemaining && !acceptedEncodings.isEmpty()) {
                contentEncoding = acceptedEncodings.first();
            } else {
                // no acceptable encoding can be sent -> return NOT ACCEPTABLE status code back to the client
                throw new NotAcceptableException();
            }
        }

        // finally set the header - but no need to set for identity encoding
        if (!IDENTITY_ENCODING.equals(contentEncoding)) {
            response.getHeaders().putSingle(HttpHeaders.CONTENT_ENCODING, contentEncoding);
        }
    }

EncodingFilter過濾器的filter()方法通過對(duì)請(qǐng)求頭信息"Accept-Encoding"的分析,先后為響應(yīng)頭信息"Vary"和"Content-Encoding"賦值,以實(shí)現(xiàn)編碼部分的內(nèi)容協(xié)商。

4、ClientResponseFilter

客戶端響應(yīng)過濾器(ClientResponseFilter)定義的過濾方法filter()包含兩個(gè)參數(shù),一個(gè)是客戶端請(qǐng)求上下文類ClientRequestContext,另一個(gè)是客戶端響應(yīng)的上下文類ClientResponseContext。ClientResponseFilter接口的實(shí)現(xiàn)類如下圖所示:

image.png

我們以HTTP摘要認(rèn)證過濾器類HttpAuthenticationFilter為例。

 @Override
    public void filter(ClientRequestContext request) throws IOException {
        if ("true".equals(request.getProperty(REQUEST_PROPERTY_FILTER_REUSED))) {
            return;
        }

        if (request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) {
            return;
        }

        Type operation = null;
        if (mode == HttpAuthenticationFeature.Mode.BASIC_PREEMPTIVE) {
            basicAuth.filterRequest(request);
            operation = Type.BASIC;
        } else if (mode == HttpAuthenticationFeature.Mode.BASIC_NON_PREEMPTIVE) {
            // do nothing
        } else if (mode == HttpAuthenticationFeature.Mode.DIGEST) {
            if (digestAuth.filterRequest(request)) {
                operation = Type.DIGEST;
            }
        } else if (mode == HttpAuthenticationFeature.Mode.UNIVERSAL) {

            Type lastSuccessfulMethod = uriCache.get(getCacheKey(request));
            if (lastSuccessfulMethod != null) {
                request.setProperty(REQUEST_PROPERTY_OPERATION, lastSuccessfulMethod);
                if (lastSuccessfulMethod == Type.BASIC) {
                    basicAuth.filterRequest(request);
                    operation = Type.BASIC;
                } else if (lastSuccessfulMethod == Type.DIGEST) {
                    if (digestAuth.filterRequest(request)) {
                        operation = Type.DIGEST;
                    }
                }
            }
        }

        if (operation != null) {
            request.setProperty(REQUEST_PROPERTY_OPERATION, operation);
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評(píng)論 19 139
  • 本文包括:1、Filter簡(jiǎn)介2、Filter是如何實(shí)現(xiàn)攔截的?3、Filter開發(fā)入門4、Filter的生命周期...
    廖少少閱讀 7,520評(píng)論 3 56
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,300評(píng)論 4 61
  • 我是空的,啊哈哈哈哈哈吧 科科以后應(yīng)該會(huì)很忙吧,可能會(huì)加班到深夜或者通宵也說不定,一個(gè)品牌從概念到上市真沒那么簡(jiǎn)單...
    舒科舒科舒科閱讀 289評(píng)論 2 1
  • 下載簡(jiǎn)書也有一段時(shí)間了,一直在想怎樣開始這一旅程。 今天,結(jié)束了30個(gè)小時(shí)的連班倒工作,四肢無力,反應(yīng)遲鈍地躺在床...
    跳舞Mia閱讀 307評(píng)論 3 1

友情鏈接更多精彩內(nèi)容