Spring Webflux 源碼閱讀之 accept包

Spring Webflux --accept

RequestedContentTypeResolver策略和實現(xiàn)來解析給定請求的請求內(nèi)容類型贴见。

typeResovle.jpg

接口

RequestedContentTypeResolver

為ServerWebExchange解決請求的媒體類型的策略狱意。


public interface RequestedContentTypeResolver {

    /**
     * Resolve the given request to a list of requested media types. The returned
     * list is ordered by specificity first and by quality parameter second.
     * @param exchange the current exchange
     * @return the requested media types or an empty list
     * @throws NotAcceptableStatusException if the requested media type is invalid
     */
    List<MediaType> resolveMediaTypes(ServerWebExchange exchange);

}

將給定的請求解析為請求的媒體類型列表。返回的列表首先按特異性排序筋遭,然后按質(zhì)量參數(shù)排序。

org.springframework.web.reactive.accept.FixedContentTypeResolver

解析器始終解析為媒體類型的固定列表暴拄。這可以用作“最后一行”策略漓滔,當客戶端沒有請求任何媒體類型時提供回調(diào)


public class FixedContentTypeResolver implements RequestedContentTypeResolver {

    private static final Log logger = LogFactory.getLog(FixedContentTypeResolver.class);


    private final List<MediaType> mediaTypes;


    /**
     * 具有單個默認MediaType的構造函數(shù)。.
     */
    public FixedContentTypeResolver(MediaType mediaType) {
        this(Collections.singletonList(mediaType));
    }

    /**
     * 使用默認的MediaType排序列表的構造函數(shù)返回用于支持各種內(nèi)容類型的應用程序乖篷。
     如果目標不存在响驴,并且不支持任何其他默認媒體類型,請考慮在最后附加MediaType.ALL那伐。
     */
    public FixedContentTypeResolver(List<MediaType> mediaTypes) {
        this.mediaTypes = Collections.unmodifiableList(mediaTypes);
    }


    /**
     * 返回配置的媒體類型列表踏施。
     */
    public List<MediaType> getContentTypes() {
        return this.mediaTypes;
    }


    @Override
    public List<MediaType> resolveMediaTypes(ServerWebExchange exchange) {
        if (logger.isDebugEnabled()) {
            logger.debug("Requested media types: " + this.mediaTypes);
        }
        return this.mediaTypes;
    }

}

org.springframework.web.reactive.accept.HeaderContentTypeResolver

解析器查看請求的“Accept”標頭。



public class HeaderContentTypeResolver implements RequestedContentTypeResolver {

    @Override
    public List<MediaType> resolveMediaTypes(ServerWebExchange exchange) throws NotAcceptableStatusException {
        try {
            List<MediaType> mediaTypes = exchange.getRequest().getHeaders().getAccept();
            MediaType.sortBySpecificityAndQuality(mediaTypes);
            return mediaTypes;
        }
        catch (InvalidMediaTypeException ex) {
            String value = exchange.getRequest().getHeaders().getFirst("Accept");
            throw new NotAcceptableStatusException(
                    "Could not parse 'Accept' header [" + value + "]: " + ex.getMessage());
        }
    }

}

org.springframework.web.reactive.accept.ParameterContentTypeResolver

解析查詢參數(shù)并使用它查找匹配的MediaType的解析器罕邀。查找鍵可以注冊畅形,也可以作為一個后備的MediaTypeFactory執(zhí)行查找。


public class ParameterContentTypeResolver implements RequestedContentTypeResolver {

    /** Primary lookup for media types by key (e.g. "json" -> "application/json") */
    private final Map<String, MediaType> mediaTypes = new ConcurrentHashMap<>(64);

    private String parameterName = "format";


    public ParameterContentTypeResolver(Map<String, MediaType> mediaTypes) {
        mediaTypes.forEach((key, value) -> this.mediaTypes.put(formatKey(key), value));
    }

    private static String formatKey(String key) {
        return key.toLowerCase(Locale.ENGLISH);
    }


    /**
     * Set the name of the parameter to use to determine requested media types.
     * <p>By default this is set to {@literal "format"}.
     */
    public void setParameterName(String parameterName) {
        Assert.notNull(parameterName, "'parameterName' is required");
        this.parameterName = parameterName;
    }

    public String getParameterName() {
        return this.parameterName;
    }


    @Override
    public List<MediaType> resolveMediaTypes(ServerWebExchange exchange) throws NotAcceptableStatusException {
        String key = exchange.getRequest().getQueryParams().getFirst(getParameterName());
        if (!StringUtils.hasText(key)) {
            return Collections.emptyList();
        }
        key = formatKey(key);
        MediaType match = this.mediaTypes.get(key);
        if (match == null) {
            match = MediaTypeFactory.getMediaType("filename." + key)
                    .orElseThrow(() -> {
                        List<MediaType> supported = new ArrayList<>(this.mediaTypes.values());
                        return new NotAcceptableStatusException(supported);
                    });
        }
        this.mediaTypes.putIfAbsent(key, match);
        return Collections.singletonList(match);
    }



org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder

Builder的復合RequestedContentTypeResolver代表其他解析器實現(xiàn)一個不同的策略來確定請求的內(nèi)容類型,例如Accept標頭,查詢參數(shù),或其他诉探。
使用生成器方法在所需的順序中添加解析器日熬。對于給定的請求,他首先解析返回一個非空的列表肾胯,并且不包含只是MediaType竖席。將使用耘纱。
默認情況下,如果沒有顯式解析器配置,構建器將添加HeaderContentTypeResolver。


public class RequestedContentTypeResolverBuilder {

    private final List<Supplier<RequestedContentTypeResolver>> candidates = new ArrayList<>();


    /**
     * Add a resolver to get the requested content type from a query parameter.
     * By default the query parameter name is {@code "format"}.
     */
    public ParameterResolverConfigurer parameterResolver() {
        ParameterResolverConfigurer parameterBuilder = new ParameterResolverConfigurer();
        this.candidates.add(parameterBuilder::createResolver);
        return parameterBuilder;
    }

    /**
     * Add resolver to get the requested content type from the
     * {@literal "Accept"} header.
     */
    public void headerResolver() {
        this.candidates.add(HeaderContentTypeResolver::new);
    }

    /**
     * Add resolver that returns a fixed set of media types.
     * @param mediaTypes the media types to use
     */
    public void fixedResolver(MediaType... mediaTypes) {
        this.candidates.add(() -> new FixedContentTypeResolver(Arrays.asList(mediaTypes)));
    }

    /**
     * Add a custom resolver.
     * @param resolver the resolver to add
     */
    public void resolver(RequestedContentTypeResolver resolver) {
        this.candidates.add(() -> resolver);
    }

    /**
     * Build a {@link RequestedContentTypeResolver} that delegates to the list
     * of resolvers configured through this builder.
     */
    public RequestedContentTypeResolver build() {

        List<RequestedContentTypeResolver> resolvers =
                this.candidates.isEmpty() ?
                        Collections.singletonList(new HeaderContentTypeResolver()) :
                        this.candidates.stream().map(Supplier::get).collect(Collectors.toList());

        return exchange -> {
            for (RequestedContentTypeResolver resolver : resolvers) {
                List<MediaType> type = resolver.resolveMediaTypes(exchange);
                if (type.isEmpty() || (type.size() == 1 && type.contains(MediaType.ALL))) {
                    continue;
                }
                return type;
            }
            return Collections.emptyList();
        };
    }


    /**
     * Helper to create and configure {@link ParameterContentTypeResolver}.
     */
    public static class ParameterResolverConfigurer {

        private final Map<String, MediaType> mediaTypes = new HashMap<>();

        @Nullable
        private String parameterName;

        /**
         * Configure a mapping between a lookup key (extracted from a query
         * parameter value) and a corresponding {@code MediaType}.
         * @param key the lookup key
         * @param mediaType the MediaType for that key
         */
        public ParameterResolverConfigurer mediaType(String key, MediaType mediaType) {
            this.mediaTypes.put(key, mediaType);
            return this;
        }

        /**
         * Map-based variant of {@link #mediaType(String, MediaType)}.
         * @param mediaTypes the mappings to copy
         */
        public ParameterResolverConfigurer mediaType(Map<String, MediaType> mediaTypes) {
            this.mediaTypes.putAll(mediaTypes);
            return this;
        }

        /**
         * Set the name of the parameter to use to determine requested media types.
         * <p>By default this is set to {@literal "format"}.
         */
        public ParameterResolverConfigurer parameterName(String parameterName) {
            this.parameterName = parameterName;
            return this;
        }

        /**
         * Private factory method to create the resolver.
         */
        private RequestedContentTypeResolver createResolver() {
            ParameterContentTypeResolver resolver = new ParameterContentTypeResolver(this.mediaTypes);
            if (this.parameterName != null) {
                resolver.setParameterName(this.parameterName);
            }
            return resolver;
        }
    }

}
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毕荐,一起剝皮案震驚了整個濱河市束析,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌憎亚,老刑警劉巖员寇,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異第美,居然都是意外死亡蝶锋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門什往,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扳缕,“玉大人,你說我怎么就攤上這事别威∏颍” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵省古,是天一觀的道長庸毫。 經(jīng)常有香客問我,道長衫樊,這世上最難降的妖魔是什么飒赃? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮科侈,結果婚禮上载佳,老公的妹妹穿的比我還像新娘。我一直安慰自己臀栈,他們只是感情好蔫慧,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著权薯,像睡著了一般姑躲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盟蚣,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天黍析,我揣著相機與錄音,去河邊找鬼屎开。 笑死阐枣,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蔼两,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼甩鳄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了额划?” 一聲冷哼從身側響起妙啃,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俊戳,沒想到半個月后彬祖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡品抽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了甜熔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圆恤。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖腔稀,靈堂內(nèi)的尸體忽然破棺而出盆昙,到底是詐尸還是另有隱情,我是刑警寧澤焊虏,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布淡喜,位于F島的核電站,受9級特大地震影響诵闭,放射性物質(zhì)發(fā)生泄漏炼团。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一疏尿、第九天 我趴在偏房一處隱蔽的房頂上張望瘟芝。 院中可真熱鬧,春花似錦褥琐、人聲如沸锌俱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贸宏。三九已至,卻和暖如春磕洪,著一層夾襖步出監(jiān)牢的瞬間吭练,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工析显, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留线脚,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像浑侥,于是被迫代替她去往敵國和親姊舵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容