RestTemplate.exchange-Nutzung

Vorwort

In unserer täglichen Entwicklung ist es unvermeidlich, eine HTTP-Anfrage zu initiieren, unabhängig davon, ob es sich um einen Anruf zwischen internen Diensten oder einem Drittanbieterdienst handelt. Zu den gängigen Methoden zum Initiieren einer HTTP-Anfrage in Java gehören native HttpURLConnection, Apaches HttpClient, Springs RestTemplate usw. Wenn Sie auf dem Spring-Framework basieren, wird dringend empfohlen, RestTemplate zu verwenden. Der Grund ist sehr einfach: Es entspricht sehr unserer Gewohnheit, HTTP-Anfragen zu initiieren, genau wie bei der Verwendung von Postman, Sie müssen sich nur um das Spezifische kümmern URL, Header, Text usw. für die mühsamen Details RestTemplate Helfen Sie uns, das Paket klar zu ordnen, wir müssen uns nicht um irrelevante Details kümmern! Insbesondere kann man sagen, dass die Methode RestTemplate.exchange andere Methoden mit nur einem Zug schlagen kann. . . In diesem Artikel werden daher die verschiedenen Verwendungsmöglichkeiten von RestTemplate.exchange im Detail vorgestellt und versucht, verschiedene Szenarien in der täglichen Entwicklung abzudecken. Beginnen wir mit ~~

1. Anfrage einholen

Hier sind 5 häufige Szenarien: 1.1 Basistyp zurückgeben , 1.2 benutzerdefinierten Objekttyp zurückgeben, 1.3 Listentyp zurückgeben, 1.4 Kartentyp zurückgeben , 1.5 benutzerdefinierten generischen Typ zurückgeben

1.1 Basistyp zurückgeben

Lassen Sie uns zunächst eine grundlegende API simulieren: Rufen Sie den Namensaufrufcode entsprechend der Benutzer-ID ab :

    // 1.1 get请求返回基本类型
    @GetMapping("/name")
    public String getName(@RequestParam("id") Integer id) {
    
    
        String url = "http://localhost:8080/demo/name/mock?id=" + id;
        return restTemplate.exchange(url, HttpMethod.GET, null, String.class).getBody();
    }

Der aufgerufene Mock-Code :

    @GetMapping("/name/mock")
    public String mockName(@RequestParam("id") Integer id) {
    
    
        return "天罡" + id;
    }

Überprüfen: Wie erwartet muss ich sagen, dass es so prägnant und gut sein sollte ~~

Fordern Sie http://localhost:8080/demo/name?id=123 an und geben Sie Tiangang 123 zurück

Zur Beschreibung der Methodenparameter von Exchange : Schauen Sie sich einfach die Kommentare des Quellcodes an, es ist sehr klar, außerdem ist es überflüssig.

Fügen Sie hier eine Bildbeschreibung ein

1.2 Benutzerdefinierten Objekttyp zurückgeben

Tatsächlich ist das benutzerdefinierte Objekt dasselbe wie der String-Aufruf, 只需要将返回类型String.class改成DTO.class即可zum Beispiel: Benutzerinformationen entsprechend der Benutzer-ID abrufen

Erstellen Sie ein neues UserDto-Objekt:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserDto implements Serializable {
    
    
    private Integer id;
    private String name;
    private Integer age;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date birthday;
}

Rufcode :

    // 1.2 get请求返回对象类型
    @GetMapping("/user")
    public UserDto getUser(@RequestParam("id") Integer id) {
    
    
        String url = "http://localhost:8080/demo/user/mock?id=" + id;
        return restTemplate.exchange(url, HttpMethod.GET, null, UserDto.class).getBody();
    }

Der aufgerufene Mock-Code :

    @GetMapping("/user/mock")
    public UserDto mockUser(@RequestParam("id") Integer id) {
    
    
        return UserDto.builder().id(id)
                .name("天罡" + id)
                .age(id + 18)
                .birthday(new Date()).build();
    }

Überprüfen Sie es: ok~~

Anfrage http://localhost:8080/demo/user?id=1 gibt { „id“: 1, „name“: „Tiangang“, „age“: 19, „birthday“: „2022-11-06 05: 35:43”}

1.3 Rückgabelistentyp

Für generische Typen müssen wir eine andere überladene Austauschmethode für generische Typen verwenden . 将responseType换成ParameterizedTypeReferenceEs wird weiterhin empfohlen, die Kommentare zum Quellcode zu lesen:

Fügen Sie hier eine Bildbeschreibung ein

Als Nächstes simulieren wir weiterhin ein häufiges Szenario: Fuzzy-Abruf aller übereinstimmenden Benutzer entsprechend dem Benutzernamen . Das zurückgegebene Ergebnis ist mehrfach und wir verwenden den Listentyp. Rufcode :通过ParameterizedTypeReference指定返回的List

    // 1.3 get请求返回List<T>类型
    @GetMapping("/user/list")
    public List<UserDto> getUserList(@RequestParam("name") String name) {
    
    
        String url = "http://localhost:8080/demo/user/list/mock?name=" + name;
        ParameterizedTypeReference<List<UserDto>> responseBodyType = new ParameterizedTypeReference<List<UserDto>>() {
    
    };
        return restTemplate.exchange(url, HttpMethod.GET, null, responseBodyType).getBody();
    }

Der aufgerufene Mock-Code :

    @GetMapping("/user/list/mock")
    public List<UserDto> mockUserList(@RequestParam("name") String name) {
    
    
        List<UserDto> list = new ArrayList<>();
        for (int i = 1; i < 3; i++) {
    
    
            list.add(UserDto.builder().id(i)
                    .name(name + i)
                    .age(i + 10)
                    .birthday(new Date()).build());
        }
        return list;
    }

Überprüfen Sie es: ok~~

Fordern Sie http://localhost:8080/demo/user/list?name=Tiangang return [ { „id“: 1, „name“: „Tiangang 1“, „age“: 11, „birthday“: „2022-11 -06 21:44:24“ }, { „id“: 2, „name“: „Tiangang 2“, „age“: 12, „birthday“: „2022-11-06 21:44:24“ } ]

1.4 Rückgabetyp Map<K,V>

Map ist ebenfalls ein generischer Typ und verfügt über zwei Typen von K und V. Wir simulieren weiterhin ein häufiges Szenario: Je nach Schlüsselwortsuche geben verschiedene Typen unterschiedliche Felder zurück . Da die zurückgegebenen Ergebnisfelder nicht festgelegt sind, geben wir den Typ Map zurück. Rufcode :依然通过ParameterizedTypeReference指定返回的Map

    // 1.4 get请求返回Map类型
    @GetMapping("/user/map")
    public Map<String, Object> getUserMap(@RequestParam(value = "type", required = true) Integer type, @RequestParam("key") String key) {
    
    
        String url = "http://localhost:8080/demo/user/map/mock?type=" + type + "&key=" + key;
        ParameterizedTypeReference<Map<String, Object>> responseBodyType = new ParameterizedTypeReference<Map<String, Object>>() {
    
    };
        return restTemplate.exchange(url, HttpMethod.GET, null, responseBodyType).getBody();
    }

Der aufgerufene Mock-Code :

    @GetMapping("/user/map/mock")
    public Map<String, Object> mockUserMap(@RequestParam(value = "type", required = true) Integer type, @RequestParam("key") String key) {
    
    
        Map<String, Object> map = new HashMap<>();
        if (type.equals(1)) {
    
    
            map.put("id", 1);
            map.put("name" + type, "hello" + key);
        } else {
    
    
            map.put("id", 2);
            map.put("name" + type, "hello" + key);
        }
        return map;
    }

Überprüfen Sie es: Je nach Typ werden unterschiedliche Felder zurückgegeben, wunderschön ~~

Anfrage http://localhost:8080/demo/user/map?type=1&key=123 gibt { „id“: 1, „ „ name1: „hello123“ } zurück

Anfrage http://localhost:8080/demo/user/map?type=2&key=456 gibt { „id“: 2, „ „ name2: „hello456“ } zurück

1.5 Benutzerdefinierten generischen Typ zurückgeben

Wir haben das Szenario des Erhaltens von Benutzerinformationen basierend auf der Benutzer-ID in 1.2 simuliert , das einen benutzerdefinierten Objekttyp zurückgibt . Als Nächstes passen wir jedoch ein generisches Rückgabeergebnis eines benutzerdefinierten benutzerdefinierten Codes an und nehmen einige Verbesserungen an 1.2 vor: Erhalten Sie Benutzer anhand der Benutzer-ID Informationen: Je nach Situation werden unterschiedliche Codes zurückgegeben .未处理非法请求、异常等情况

Wir erstellen eine neue Ergebnisklasse:

@Data
public class Result<T extends Serializable> implements Serializable {
    
    
    private boolean success;
    private String code;
    private String message;
    private T data;

    public static <T extends Serializable> Result<T> success(String code, String message, T data) {
    
    
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMessage(message);
        result.setData(data);
        result.setSuccess(true);
        return result;
    }

    public static <T extends Serializable> Result<T> success(T data) {
    
    
        return success("200", "成功", data);
    }

    public static <T extends Serializable> Result<T> fail(String code, String message) {
    
    
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMessage(message);
        result.setSuccess(false);
        return result;
    }
}

Rufcode :依然通过ParameterizedTypeReference指定返回的Result<T>

    // 1.5 get请求返回自定义泛型类型
    @GetMapping("/user/result")
    public Result<UserDto> getUserResult(@RequestParam("id") Integer id) {
    
    
        String url = "http://localhost:8080/demo/user/result/mock?id=" + id;
        ParameterizedTypeReference<Result<UserDto>> responseBodyType = new ParameterizedTypeReference<Result<UserDto>>() {
    
    };
        return restTemplate.exchange(url, HttpMethod.GET, null, responseBodyType).getBody();
    }

Der aufgerufene Mock-Code :

	@GetMapping("/user/result/mock")
    public Result<UserDto> mockUserResult(@RequestParam("id") Integer id) {
    
    
        if (id == null || id <= 0) {
    
    
            return Result.fail("400", "id不合法!");
        }
        if (id % 2 == 0) {
    
    
            // 这里只是模拟异常情况
            return Result.fail("500", "操作失败,访问量太大了!");
        }
        UserDto userDto = UserDto.builder().id(id)
                .name("天罡" + id)
                .age(id + 18)
                .birthday(new Date()).build();
        return Result.success("200", "成功", userDto);
    }

Schauen Sie es sich an: genau das, was wir wollten, genau wie erwartet!

Anfrage http://localhost:8080/demo/user/result?id=0 return { „success“: false, „code“: „400“, „message“: „id is invalid!“, „data“: null }

Anfrage http://localhost:8080/demo/user/result?id=1 gibt { „success“: true, „code“: „200“, „message“: „success“, „data“: { „id“ zurück : 1, „name“: „Tiangang 1“, „age“: 19, „birthday“: „2022-11-07 04:03:09“ } }

Anfrage http://localhost:8080/demo/user/result?id=2 return { „success“: false, „code“: „500“, „message“: „Vorgang fehlgeschlagen, zu viele Besuche!“, „data ": Null }


2. Anfrage posten

Tatsächlich ist die Verwendung von POST und GET für den Austausch sehr ähnlich, daher werden hier nur zwei Demos vorbereitet, um zu demonstrieren, wie man Header und Body übergibt . 2.1 Übergeben Sie Header+Body, um den Objekttyp zurückzugeben. 2.2 Übergeben Sie Header+Body, um den benutzerdefinierten generischen Typ zurückzugeben

2.1 Übergeben Sie Header + Body und geben Sie den Objekttyp zurück

Rufcode :

	@GetMapping("/user/body")
    public UserDto postUser(@RequestParam("id") Integer id) {
    
    
        String url = "http://localhost:8080/demo/user/body/mock";
        UserDto body = UserDto.builder().id(id)
                .name("body" + id)
                .age(id + 18)
                .birthday(new Date()).build();
        // header根据实际情况设置,没有就空着
        HttpHeaders headers = new HttpHeaders();
        headers.add("AccessKey", "自定义的API访问key");
        headers.add("Content-Type", "application/json");
        HttpEntity<?> requestEntity = new HttpEntity<>(body, headers);
        return restTemplate.exchange(url, HttpMethod.POST, requestEntity, UserDto.class).getBody();
    }

Der aufgerufene Mock-Code :

    @PostMapping("/user/body/mock")
    public UserDto mockPostUser(@RequestBody UserDto userParam) {
    
    
        return userParam;
    }

Überprüfen Sie es: ok~~

Anfrage http://localhost:8080/demo/user/body?id=1 gibt { „id“: 1, „name“: „body1“, „age“: 19, „birthday“: „2022-11-06 21:20:41“}

2.2 Übergeben Sie Header+Body, um einen benutzerdefinierten generischen Typ zurückzugeben

Der Unterschied zur Rückgabe eines normalen Typs ist 将responseType换成ParameterizedTypeReference der aufrufende Code :

@GetMapping("/user/result/body")
public Result<UserDto> postUserResult(@RequestParam("id") Integer id) {
    
    
    String url = "http://localhost:8080/demo/user/result/body/mock";
    UserDto body = UserDto.builder().id(id)
            .name("body" + id)
            .age(id + 10)
            .birthday(new Date()).build();
    // header根据实际情况设置,没有就空着
    HttpHeaders headers = new HttpHeaders();
    headers.add("AccessKey", "自定义的API访问key");
    headers.add("Content-Type", "application/json");
    HttpEntity<?> requestEntity = new HttpEntity<>(body, headers);
    ParameterizedTypeReference<Result<UserDto>> responseBodyType = new ParameterizedTypeReference<Result<UserDto>>(){
    
    };
    return restTemplate.exchange(url, HttpMethod.POST, requestEntity, responseBodyType).getBody();
}

Der aufgerufene Mock-Code :

    @PostMapping("/user/result/body/mock")
    public Result<UserDto> mockPostUserResult(@RequestBody UserDto userParam) {
    
    
        return Result.success("200", "成功", userParam);
    }

Überprüfen Sie es: ok~~

Anfrage http://localhost:8080/demo/user/body?id=1 gibt { „success“: true, „code“: „200“, „message“: „success“, „data“: { „id“ zurück : 1, „Name“: „Körper1“, „Alter“: 11, „Geburtstag“: „2022-11-06 21:25:25“ } }


3. Umgang mit ungewöhnlichen Situationen

Keine der oben beschriebenen Ausnahmen behandelt Ausnahmen. Normalerweise behandeln wir zwei Ausnahmen:

  1. löst selbst eine RestClientException aus
  2. Der Code der zurückgegebenen ResponseEntity ist ungleich 200

Häufigste Art:

public <T> T restForEntity(HttpMethod httpMethod, String url, HttpHeaders headers, Object body
			, Class<T> responseType) {
    
    
    HttpEntity<?> requestEntity = null;
    if (headers != null || body != null) {
    
    
        requestEntity = new HttpEntity<>(body, headers);
    }
    try {
    
    
        ResponseEntity<T> responseEntity = restTemplate.exchange(url, httpMethod, requestEntity, responseType);
        if (responseEntity.getStatusCode().equals(HttpStatus.OK)) {
    
    
            return responseEntity.getBody();
        } else {
    
    
            // 处理Code不等于200的情况
            System.out.println("返回结果不等于200:code=" + responseEntity.getStatusCode().value()
                    + " reason=" + responseEntity.getStatusCode().getReasonPhrase());
        }
    } catch (RestClientException e) {
    
    
        // 处理RestClientException
        e.printStackTrace();
    }
    return null;
}

Allgemeiner Typ: 只需要将普通类型的入参Class<T>改成 ParameterizedTypeReference<T>

public <T> T restForWarpEntity(HttpMethod httpMethod, String url, HttpHeaders headers, Object body
				, ParameterizedTypeReference<T> responseBodyType) {
    
    
    HttpEntity<?> requestEntity = null;
    if (headers != null || body != null) {
    
    
        requestEntity = new HttpEntity<>(body, headers);
    }
    try {
    
    
        ResponseEntity<T> responseEntity = restTemplate.exchange(url, httpMethod, requestEntity, responseBodyType);
        if (responseEntity.getStatusCode().equals(HttpStatus.OK)) {
    
    
            return responseEntity.getBody();
        } else {
    
    
            // 处理Code不等于200的情况, 这里只简单打印
            System.out.println("返回结果不等于200:code=" + responseEntity.getStatusCode().value()
                    + " reason=" + responseEntity.getStatusCode().getReasonPhrase());
        }
    } catch (RestClientException e) {
    
    
        // 处理RestClientException, 这里只简单打印
        e.printStackTrace();
    }
    return null;
}

4. RestTemplate-Bereitstellung @Bean

@Configuration
public class RestTemplateConfig {
    
    
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
    
    
        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
        restTemplate.getMessageConverters()
                .stream()
                .filter(MappingJackson2HttpMessageConverter.class::isInstance)
                .map(MappingJackson2HttpMessageConverter.class::cast)
                .findFirst()
                .map(MappingJackson2HttpMessageConverter::getObjectMapper)
                .ifPresent(objectMapper -> {
    
    
                    // 去掉默认的时间戳格式
                    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
                    // 设置为东八区
                    objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
                    // 序列化时,日期的统一格式
                    objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
                    // 忽略大小写
                    objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
                });

        return restTemplate;
    }

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory(){
    
    
        // 如果使用okHttpClient需要引入jar包:okhttp
        // OkHttp3ClientHttpRequestFactory factory = new OkHttp3ClientHttpRequestFactory();
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(15000);
        factory.setReadTimeout(30000);
        return factory;
    }
}

Je suppose que tu aimes

Origine blog.csdn.net/ximaiyao1984/article/details/132257881
conseillé
Classement