国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

SpringBoot | RestTemplate異常處理器ErrorHandler使用詳解

這篇具有很好參考價(jià)值的文章主要介紹了SpringBoot | RestTemplate異常處理器ErrorHandler使用詳解。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

關(guān)注wx:CodingTechWork

引言

??在代碼開發(fā)過程中,發(fā)現(xiàn)很多地方通過RestTemplate調(diào)用了第三方接口,而第三方接口需要根據(jù)某些狀態(tài)碼或者異常進(jìn)行重試調(diào)用,此時(shí),要么在每個(gè)調(diào)用的地方進(jìn)行異常捕獲,然后重試;要么在封裝的RestTemplate工具類中進(jìn)行統(tǒng)一異常捕獲和封裝。當(dāng)然,本文不走尋常路,將會(huì)通過RestTemplate的異常處理器進(jìn)行操作。文章來源地址http://www.zghlxwxcb.cn/news/detail-654719.html

RestTemplate異常處理器介紹

分類

異常處理器 功能描述
ResponseErrorHandler 異常處理器接口,是restTemplate所有異常處理器的實(shí)現(xiàn)接口
DefaultResponseErrorHandler 默認(rèn)的異常處理器,處理客戶端和服務(wù)端異常
ExtractingResponseErrorHandler 將HTTP錯(cuò)誤響應(yīng)轉(zhuǎn)換RestClientException
NoOpResponseErrorHandler 不處理異常

RestTemplate異常處理器源碼

ResponseErrorHandler

public interface ResponseErrorHandler {
	/**
	 * 判斷請(qǐng)求是否異常
	 * false: 請(qǐng)求返回?zé)o錯(cuò)誤
	 * true: 請(qǐng)求返回有錯(cuò)誤
	 * 可定制化根據(jù)某一些status的值進(jìn)行返回,如根據(jù)2xx返回false,非2xx返回true
	 * 同時(shí),可根據(jù)ClientHttpResponse的返回結(jié)果來定制化判斷
	 */
    boolean hasError(ClientHttpResponse var1) throws IOException;
	/**
	 * 處理錯(cuò)誤
	 */
    void handleError(ClientHttpResponse var1) throws IOException;
	/**
	 * 默認(rèn)的異常處理方法
	 */
    default void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
    	//默認(rèn)調(diào)用handleError(response)方法,也可以重寫該方法
        this.handleError(response);
    }
}

DefaultResponseErrorHandler

package org.springframework.web.client;

import java.io.IOException;
import java.nio.charset.Charset;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus.Series;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.FileCopyUtils;

public class DefaultResponseErrorHandler implements ResponseErrorHandler {
    public DefaultResponseErrorHandler() {
    }
	/**
	 * 判斷請(qǐng)求是否異常
	 */
    public boolean hasError(ClientHttpResponse response) throws IOException {
        HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
        //statusCode不為空并調(diào)用受保護(hù)的方法hasError()方法
        return statusCode != null && this.hasError(statusCode);
    }

    protected boolean hasError(HttpStatus statusCode) {
    	//遇到客戶端錯(cuò)誤4xx或服務(wù)端錯(cuò)誤5xx,就返回true表示有錯(cuò)誤
        return statusCode.series() == Series.CLIENT_ERROR || statusCode.series() == Series.SERVER_ERROR;
    }
	/**
	 * 處理錯(cuò)誤
	 */
    public void handleError(ClientHttpResponse response) throws IOException {
    	//獲取狀態(tài)碼
        HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
        if (statusCode == null) {
            throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
        } else {
        	//狀態(tài)碼不為空,則處理錯(cuò)誤(主要是4xx和5xx錯(cuò)誤)
            this.handleError(response, statusCode);
        }
    }
	/**
	 * 處理錯(cuò)誤
	 */
    protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
        String statusText = response.getStatusText();
        HttpHeaders headers = response.getHeaders();
        byte[] body = this.getResponseBody(response);
        Charset charset = this.getCharset(response);
        switch(statusCode.series()) {
        case CLIENT_ERROR:
        	//http客戶端錯(cuò)誤
            throw HttpClientErrorException.create(statusCode, statusText, headers, body, charset);
        case SERVER_ERROR:
        	//http服務(wù)端錯(cuò)誤
            throw HttpServerErrorException.create(statusCode, statusText, headers, body, charset);
        default:
            throw new UnknownHttpStatusCodeException(statusCode.value(), statusText, headers, body, charset);
        }
    }

    /** @deprecated */
    @Deprecated
    protected HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOException {
        HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
        if (statusCode == null) {
            throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
        } else {
            return statusCode;
        }
    }

    protected byte[] getResponseBody(ClientHttpResponse response) {
        try {
            return FileCopyUtils.copyToByteArray(response.getBody());
        } catch (IOException var3) {
            return new byte[0];
        }
    }

    @Nullable
    protected Charset getCharset(ClientHttpResponse response) {
        HttpHeaders headers = response.getHeaders();
        MediaType contentType = headers.getContentType();
        return contentType != null ? contentType.getCharset() : null;
    }
}

ExtractingResponseErrorHandler

package org.springframework.web.client;

import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatus.Series;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;

public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler {
	//定義HttpMessageConverter對(duì)象列表
    private List<HttpMessageConverter<?>> messageConverters = Collections.emptyList();
    private final Map<HttpStatus, Class<? extends RestClientException>> statusMapping = new LinkedHashMap();
    private final Map<Series, Class<? extends RestClientException>> seriesMapping = new LinkedHashMap();

    public ExtractingResponseErrorHandler() {
    }

    public ExtractingResponseErrorHandler(List<HttpMessageConverter<?>> messageConverters) {
        this.messageConverters = messageConverters;
    }

    public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        this.messageConverters = messageConverters;
    }

    public void setStatusMapping(Map<HttpStatus, Class<? extends RestClientException>> statusMapping) {
        if (!CollectionUtils.isEmpty(statusMapping)) {
            this.statusMapping.putAll(statusMapping);
        }

    }

    public void setSeriesMapping(Map<Series, Class<? extends RestClientException>> seriesMapping) {
        if (!CollectionUtils.isEmpty(seriesMapping)) {
            this.seriesMapping.putAll(seriesMapping);
        }

    }

    protected boolean hasError(HttpStatus statusCode) {
        if (this.statusMapping.containsKey(statusCode)) {
            return this.statusMapping.get(statusCode) != null;
        } else if (this.seriesMapping.containsKey(statusCode.series())) {
            return this.seriesMapping.get(statusCode.series()) != null;
        } else {
            return super.hasError(statusCode);
        }
    }

    public void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
        if (this.statusMapping.containsKey(statusCode)) {
            this.extract((Class)this.statusMapping.get(statusCode), response);
        } else if (this.seriesMapping.containsKey(statusCode.series())) {
            this.extract((Class)this.seriesMapping.get(statusCode.series()), response);
        } else {
            super.handleError(response, statusCode);
        }

    }
	//轉(zhuǎn)換抽取為RestClientException異常
    private void extract(@Nullable Class<? extends RestClientException> exceptionClass, ClientHttpResponse response) throws IOException {
        if (exceptionClass != null) {
            HttpMessageConverterExtractor<? extends RestClientException> extractor = new HttpMessageConverterExtractor(exceptionClass, this.messageConverters);
            RestClientException exception = (RestClientException)extractor.extractData(response);
            if (exception != null) {
                throw exception;
            }
        }
    }
}

NoOpResponseErrorHandler

	//在TestRestTemplate類中
    private static class NoOpResponseErrorHandler extends DefaultResponseErrorHandler {
        private NoOpResponseErrorHandler() {
        }
		//不做錯(cuò)誤處理
        public void handleError(ClientHttpResponse response) throws IOException {
        }
    }

RestTemplate異常處理器被觸發(fā)源碼

  1. 初始化errorHandler變量
    SpringBoot | RestTemplate異常處理器ErrorHandler使用詳解,# Spring Boot框架,開發(fā)模板總結(jié),JAVA核心技術(shù),spring boot,iphone,后端
  2. 執(zhí)行doExecute()方法
    @Nullable
    protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
        Assert.notNull(url, "URI is required");
        Assert.notNull(method, "HttpMethod is required");
        ClientHttpResponse response = null;

        Object var14;
        try {
            ClientHttpRequest request = this.createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }

            response = request.execute();
            //處理響應(yīng)
            this.handleResponse(url, method, response);
            var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;
        } catch (IOException var12) {
            String resource = url.toString();
            String query = url.getRawQuery();
            resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;
            throw new ResourceAccessException("I/O error on " + method.name() + " request for \"" + resource + "\": " + var12.getMessage(), var12);
        } finally {
            if (response != null) {
                response.close();
            }

        }

        return var14;
    }
  1. 處理響應(yīng),調(diào)用handleResponse()方法
    protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
    	//獲取異常處理器
        ResponseErrorHandler errorHandler = this.getErrorHandler();
        boolean hasError = errorHandler.hasError(response);
        if (this.logger.isDebugEnabled()) {
            try {
                int code = response.getRawStatusCode();
                HttpStatus status = HttpStatus.resolve(code);
                this.logger.debug("Response " + (status != null ? status : code));
            } catch (IOException var8) {
            }
        }

        if (hasError) {
            errorHandler.handleError(url, method, response);
        }

    }
  1. 獲取異常處理器,調(diào)用getErrorHandler()方法
    public ResponseErrorHandler getErrorHandler() {
    	//返回的就是RestTemplate中的成員變量errorHandler
        return this.errorHandler;
    }

RestTemplate異常處理器實(shí)踐模板

定義一個(gè)自定義的errorHandler實(shí)現(xiàn)ResponseErrorHandler接口

  1. errorHandler
/**
 * 繼承默認(rèn)錯(cuò)誤處理器DefaultResponseErrorHandler,無需關(guān)注hasError和handlerError方法
 */
@Component
public class MyResponseErrorHandler implements ResponseErrorHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyResponseErrorHandler.class);

    /**
     * my service進(jìn)行定制化處理
     */
    @Autowired
    private MyService myService;
    
    @Override
    public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
        return clientHttpResponse.getStatusCode().value() != 200 && clientHttpResponse.getStatusCode().value() != 201 && clientHttpResponse.getStatusCode().value() !=302;
    }

    @Override
    public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
        //遇到401進(jìn)行單獨(dú)處理
        if (HttpStatus.UNAUTHORIZED.value() == response.getStatusCode().value()) {
            myService.doSomething(url.getHost());
        } else {
        	//繼續(xù)拋異常
            throw new RuntimeException(clientHttpResponse.getStatusText());
        }
    }
    @Override
    public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
        LOGGER.error("=======================ERROR HANDLER============================");
        LOGGER.error("DateTime:{}", PoolUtil.currentTime());
        LOGGER.error("HOST:{},URI:{}", url.getHost(), url.getPath());
        LOGGER.error("Method:{}", method.name());
        LOGGER.error("Exception:{}", response.getStatusCode());
        LOGGER.error("StatusText: {}", response.getStatusText());
        LOGGER.error("========================================================");
    }
}
  1. HttpClientUtils工具類
@Slf4j
public class HttpClientUtils {
	//http協(xié)議
    private static final String HTTP_PROTOCOL = "http";
  	//https協(xié)議
    private static final String HTTPS_PROTOCAL = "https";
	//最大連接數(shù)
    private static final int MAX_CONNECT = 300;
    //默認(rèn)連接數(shù)
    private static final int DEFAULT_CONNECT = 200;
    public HttpClientUtils(){

    }
	/**
	 * new一個(gè)http client
	 */
    public static CloseableHttpClient newHttpClientForHttpsOrHttp()  {
        HttpClientBuilder build = HttpClientBuilder.create();

        try {
             SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] arg0, String arg1){
                    return true;
                }
            }).build();
            build.setSslcontext(sslContext);
            //X509HostnameVerifier校驗(yàn)
            X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() {
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }
                public void verify(String arg0, SSLSocket arg1){}
                public void verify(String arg0, String[] arg1, String[] arg2){}
                public void verify(String arg0, X509Certificate arg1){}
            };
			//SSL連接socket工廠
            SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            //http和https協(xié)議register
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
                    .register(HTTP_PROTOCOL, PlainConnectionSocketFactory.getSocketFactory()).register(HTTPS_PROTOCAL, sslSocketFactory)
                    .build();

	        //連接池
            PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            connMgr.setDefaultMaxPerRoute(DEFAULT_CONNECT_NUM);
            connMgr.setMaxTotal(MAX_CONNECT_NUM);
            build.setConnectionManager(poolingHttpClientConnectionManager);
			//構(gòu)建CloseableHttpClient
            CloseableHttpClient closeableHttpClient = build.build();
            return closeableHttpClient;
        } catch (Exception e ) {
        	log.error("異常:{}", e.getLocalizedMessage());
        }
        return null;
    }

}
  1. restTemplate調(diào)用
    /**
     * 獲取遠(yuǎn)程連接template
     *
     * @return
     */
    public static RestTemplate getRestTempte() {
        try {
            HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(HttpClientUtils.newHttpClientForHttpsOrHttp());
            factory.setConnectTimeout(30000);
            //設(shè)置handler
            return new RestTemplate(factory).setErrorHandler(new MyResponseErrorHandler());
        } catch (Exception e) {
            return null;
        }
    }

定義一個(gè)自定義的errorHandler繼承DefaultResponseErrorHandler類

/**
 * 繼承默認(rèn)錯(cuò)誤處理器DefaultResponseErrorHandler,無需關(guān)注hasError和handlerError方法
 */
@Component
public class MyResponseErrorHandler extends DefaultResponseErrorHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyResponseErrorHandler.class);

    /**
     * my service進(jìn)行定制化處理
     */
    @Autowired
    private MyService myService;

    @Override
    public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
        LOGGER.error("=======================ERROR HANDLER============================");
        LOGGER.error("DateTime:{}", PoolUtil.currentTime());
        LOGGER.error("HOST:{},URI:{}", url.getHost(), url.getPath());
        LOGGER.error("Method:{}", method.name());
        LOGGER.error("Exception:{}", response.getStatusCode());
        LOGGER.error("StatusText: {}", response.getStatusText());
        LOGGER.error("========================================================");
        //遇到401進(jìn)行單獨(dú)處理
        if (HttpStatus.UNAUTHORIZED.value() == response.getStatusCode().value()) {
            myService.doSomething(url.getHost());
        } else {
            this.handleError(response);
        }
    }
}

到了這里,關(guān)于SpringBoot | RestTemplate異常處理器ErrorHandler使用詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Spring異常處理器

    Spring異常處理器

    ?問題: ? 程序允許不免的在各層都可能會(huì)產(chǎn)生異常,我們?cè)撊绾翁幚磉@些異常? 如果只是在方法里面單獨(dú)使用 try… catch… 語句去一個(gè)一個(gè)的進(jìn)行捕捉處理的話,那毫無疑問是不現(xiàn)實(shí)的,因?yàn)楫惓?shù)量是非常龐大的并且對(duì)于異常的出現(xiàn)種類是不可預(yù)料的,于是我們可以使用

    2024年02月13日
    瀏覽(19)
  • SpringMVC之異常處理器

    SpringMVC提供了一個(gè)處理控制器方法執(zhí)行過程中所出現(xiàn)的異常的接口:HandlerExceptionResolver。 HandlerExceptionResolver接口的實(shí)現(xiàn)類有:DefaultHandlerExceptionResolver(默認(rèn)的)和 SimpleMappingExceptionResolver(自定義的)。 這里配置了兩個(gè)異常,出現(xiàn)其中一個(gè)異常后跳轉(zhuǎn)到error頁面。 以上就是異

    2024年02月10日
    瀏覽(24)
  • 13、SpringMVC之異常處理器

    13、SpringMVC之異常處理器

    創(chuàng)建名為spring_mvc_exception的新module,過程參考9.1節(jié)和9.5節(jié) SpringMVC 提供了一個(gè)處理控制器方法執(zhí)行異常的接口:HandlerExceptionResolver HandlerExceptionResolver 接口的實(shí)現(xiàn)類有:DefaultHandlerExceptionResolver 和 SimpleMappingExceptionResolver 實(shí)際工作中,有時(shí)使用 SimpleMappingExceptionResolver 異常解析器

    2024年02月05日
    瀏覽(25)
  • Spring MVC 異常處理器

    Spring MVC 異常處理器

    如果不加以異常處理,錯(cuò)誤信息肯定會(huì)拋在瀏覽器頁面上,這樣很不友好,所以必須進(jìn)行異常處理。 系統(tǒng)的dao、service、controller出現(xiàn)都通過throws Exception向上拋出,最后由springmvc前端控制器交由異常處理器進(jìn)行異常處理,如下圖: 編寫controller 在index.jsp里面定義超鏈接

    2024年01月22日
    瀏覽(28)
  • Spring MVC配置全局異常處理器?。?!
  • 【微服務(wù)網(wǎng)關(guān)---Gateway 的全局異常處理器】

    【微服務(wù)網(wǎng)關(guān)---Gateway 的全局異常處理器】

    Gateway網(wǎng)關(guān)統(tǒng)一全局異常處理操作 方便前端看到 這里要精細(xì)化翻譯,默認(rèn)返回用戶是看不懂的 所以需要配置一個(gè) Gateway 的全局異常處理器 如果沒有網(wǎng)關(guān)全局異常的 會(huì)如下截圖 代碼如下: 代碼如下: 代碼如下: 以上就是今天要講的內(nèi)容,本文僅僅簡單 所以需要配置一個(gè)

    2024年02月12日
    瀏覽(19)
  • SpringMVC的攔截器和異常處理器

    目錄 lerInterceptor 攔截器 1、攔截器的作用 2、攔截器的創(chuàng)建 3、攔截器的三個(gè)抽象方法 4、攔截器的配置 5、多個(gè)攔截器的執(zhí)行順序 SpringMVC的異常處理器 1、異常處理器概述 2、基于配置文件的異常處理 3、基于注解的異常處理 攔截器的作用時(shí)機(jī) SpringMVC的攔截器作用于? 控制器

    2024年02月02日
    瀏覽(40)
  • SpringMVC之?dāng)r截器和異常處理器

    學(xué)習(xí)的最大理由是想擺脫平庸,早一天就多一份人生的精彩;遲一天就多一天平庸的困擾。各位小伙伴,如果您: 想系統(tǒng)/深入學(xué)習(xí)某技術(shù)知識(shí)點(diǎn)… 一個(gè)人摸索學(xué)習(xí)很難堅(jiān)持,想組團(tuán)高效學(xué)習(xí)… 想寫博客但無從下手,急需寫作干貨注入能量… 熱愛寫作,愿意讓自己成為更好

    2024年02月03日
    瀏覽(20)
  • Spring Boot 如何自定義異常處理器

    Spring Boot 如何自定義異常處理器

    在Spring Boot應(yīng)用程序中,異常處理是一個(gè)非常重要的方面。如果您不處理異常,應(yīng)用程序可能會(huì)崩潰或出現(xiàn)不可預(yù)料的行為。默認(rèn)情況下,Spring Boot將未捕獲的異常返回給客戶端。這通常不是期望的行為,因?yàn)榭蛻舳丝赡軣o法理解異常信息。在本文中,我們將介紹如何在Sprin

    2024年02月06日
    瀏覽(21)
  • [ARM 匯編]進(jìn)階篇—異常處理與中斷—2.4.2 ARM處理器的異常向量表

    [ARM 匯編]進(jìn)階篇—異常處理與中斷—2.4.2 ARM處理器的異常向量表

    異常向量表簡介 在ARM架構(gòu)中,異常向量表是一組固定位置的內(nèi)存地址,它們包含了處理器在遇到異常時(shí)需要跳轉(zhuǎn)到的處理程序的入口地址。每個(gè)異常類型都有一個(gè)對(duì)應(yīng)的向量地址。當(dāng)異常發(fā)生時(shí),處理器會(huì)自動(dòng)跳轉(zhuǎn)到對(duì)應(yīng)的向量地址,并開始執(zhí)行異常處理程序。 異常向量表

    2024年02月09日
    瀏覽(89)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包