RestTemplate etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
RestTemplate etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

27 Ağustos 2022 Cumartesi

SpringMVC RestTemplate Error Handling

Giriş
Açıklaması şöyle
By default, the RestTemplate will throw one of these exceptions in the case of an HTTP error:

HttpClientErrorException – in the case of HTTP status 4xx
HttpServerErrorException – in the case of HTTP status 5xx
UnknownHttpStatusCodeException – in the case of an unknown HTTP status
All of these exceptions are extensions of RestClientResponseException.
Şeklen şöyle


Bu exception'ların fırlatılmasını sağlayan sınıf DefaultResponseErrorHandler. Örneğin olmayan bir URL'ye GET gönderirsek şu sonucu alırız
Response body
{
  "timestamp": 1661608573837,
  "status": 500,
  "error": "Internal Server Error",
  "path": "/exchangerate"
}
Hatalı apiKey gönderirse şu sonucu alırız
{
  "timestamp": 1661632797098,
  "status": 500,
  "error": "Internal Server Error",
  "path": "/exchangerate"
}
Hatalı Json gönderirsek şu sonucu alırız. Yani fırlatılan exception ismi, ve exception mesajı da var.
{
 "timestamp": 1500597044204,
 "status": 400,
 "error": "Bad Request",
 "exception": "org.springframework.http.converter.HttpMessageNotReadableException",
 "message": "JSON parse error: Unrecognized token 'three': was expecting ('true', 'false' or 'null'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'aaa': was expecting ('true', 'false' or 'null')\n at [Source: java.io.PushbackInputStream@cba7ebc; line: 4, column: 17]",
 "path": "/birds"
}
1. RestClientResponseException Sınıfı Yakalamak
Örnek - ResponseEntity 
Çıktıyı değiştirmek için bu exception'ı yakalayıp kendi istediğimiz bir ResponseEntity nesnesini dönmek gerekir. Şöyle yaparız
@ExceptionHandler(RestClientResponseException.class)
public ResponseEntity<String> restClientResponseExceptionHandler(
  RestClientResponseException exception) {

  return ResponseEntity.status(exception.getRawStatusCode())
    .body(exception.getResponseBodyAsString());
}
Hatalı apiKey gönderirse 401 alırız ve bu sefer çıktı şöyle olur
{"message":"Invalid authentication credentials"}
Örnek - Kendi Sınıfımız 
Elimizde şöyle bir kod olsun
@Data
public class ErrorResponse {

  int status;
  String message;
  String path;
}
Şöyle yaparız
@ExceptionHandler(RestClientResponseException.class)
public ResponseEntity<ErrorResponse> restClientResponseExceptionHandler(
  RestClientResponseException exception, 
  HttpServletRequest httpServletRequest ) {

  ErrorResponse errorResponse = new ErrorResponse();
  errorResponse.setStatus(exception.getRawStatusCode());
  errorResponse.setMessage(exception.getResponseBodyAsString());
  errorResponse.setPath(httpServletRequest.getRequestURI());
  return new ResponseEntity(errorResponse, 
                            HttpStatus.valueOf(exception.getRawStatusCode()));
}
401 alırız ve çıktı şöyledir
{
  "status": 401,
  "message": "{\"message\":\"Invalid authentication credentials\"}",
  "path": "/exchangerate"
}
2. Kendi Error Handler Sınıfımız
Burada amaç fırlatılan exception'nı değiştirmek. Önümüzde iki seçenek var
1. ResponseErrorHandler  arayüzünden kalıtmak
2. DefaultResponseErrorHandler sınıfından kalıtmak

Örnek - DefaultResponseErrorHandler Kalıtımı
Şöyle yaparız.
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
  @Override
   public boolean hasError(ClientHttpResponse response)
    throws IOException {
    try {
      //Do your stuff
      return super.hasError(response);
    } catch (Exception e) {
      ...;
       return true;
    }
  }

  @Override
   public void handleError(ClientHttpResponse response)
    throws IOException {
    try {
      //Do your stuff
      super.handleError(response);
    } catch (Exception e) {
      ...
      throw e;
    }
  }
});


30 Mart 2020 Pazartesi

SpringMVC RestTemplate postForEntity metodu - Form veya JSON Post Eder

Giriş
Http Post ile belirtilen sınıfı gönderir. 
- Birinci paramtre hedef url
- İkinci parametre data + header bilgisi içeren HttpEntity nesnesi
- Üçüncü parametre döndürülecek sonuç nesnesinin T tipidir. Yani Sonuç ResponseEntity<T> tipindendir. Dönüş tipi ResponseEntity<MsToken>, ResponseEntity<String> gibi bir şey olabilir

Örnek - Form Post Etmek
Login olmak için şöyle yaparız. Data MultiValueMap içine doldurulur. Dönüş tipi String
HttpHeaders httpHeaders = new HttpHeaders(); //Headers
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));

MultiValueMap<String,Object> parameters = new LinkedMultiValueMap<> (); //Data
parameters.add("username",username);
parameters.add("password",password);
parameters.add("remember-me",true);

HttpEntity<MultiValueMap<String,Object>> httpEntity = new HttpEntity (parameters,httpHeaders);
RestTemplate restTemplate = new RestTemplate();
try{
  ResponseEntity<String> responseEntity = restTemplate.postForEntity ("http:.../login",httpEntity,String.class);
  HttpHeaders header = responseEntity.getHeaders();
  ...
} catch(RestClientException ex){
  ...
}
Örnek - Form Post Etmek
Şöyle yaparız
private MsToken getToken() { // create client final RestTemplate restTemplate = new RestTemplate(); // prepare header final HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // prepare form data final MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); map.add("grant_type", "client_credentials"); map.add("client_id", appId); map.add("client_secret", appPassword); map.add("scope", "https://api.botframework.com/.default"); // prepare request final HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map,headers); // execute call final ResponseEntity<MsToken> response = restTemplate .postForEntity(MS_LOGIN_URL, request, MsToken.class); return response.getBody(); }
Örnek - MULTIPART_FORM_DATA Post Etmek
Şöyle yaparız. Data MultiValueMap içine doldurulur
public void postData(String firstname, String lastname, String fileName,
byte[] fileContents) {

  HttpHeaders headers = new HttpHeaders();
  // Set Content Type to multipart/form-data
  headers.setContentType(MediaType.MULTIPART_FORM_DATA);

  MultiValueMap<String, Object> data = new LinkedMultiValueMap<>();
  try {
    // Put form data
    data.add("firstname", firstname); //1
    data.add("lastname", lastname);   //2

    // Put image data
    ByteArrayResource fileData = new ByteArrayResource(fileContents) {
      // This override is required because the default method returns null.
      @Override
      public String getFilename() {
return fileName;
      }
    };
    HttpHeaders parts = new HttpHeaders();
    final HttpEntity<ByteArrayResource> imageEntity = new HttpEntity<>(fileData, parts);
    data.add("image", imageEntity); //3

    // POST form data
    HttpEntity<MultiValueMap<String, Object>> requestEntity =
new HttpEntity<>(data, headers);
    restTemplate.postForEntity(applicationProperties.getDocumentUploadUri(),
requestEntity, String.class);

  } catch (Exception e) {
    e.printStackTrace();
  }
}
Örnek - JSON Post Etmek
Boş bir json post göndermek için şöyle yaparız. Burada ResponseEntity<String> olarak kodladık. Karşı taraf ise boolean dönüyor. ResponseEntity nesnesinin body metodu "true" veya "false" değeri taşır.
String destinationPort = "8090";

RestTemplate restTemplate = new RestTemplate();

HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<String> entity = new HttpEntity<> (httpHeaders);

String url ="http://localhost:{port}/foo/bar"
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url,entity,
  String.class,destinationPort);
if (responseEntit.getStatusCode() == HttpStatus.OK){
  ...
}
Bu kod @RequestParam veya @RequestBody verisi göndermez. Şu kodu tetikler
@PostMapping(value="bar")
public boolean bar(){ 
...
}
Aynı şeyi exchange() ile yapmak isteseydik şöyle yapardık
ResponseEntity<String> responseEntity = restTemplate.exchange(url,HttpMethod.POST,entity,
  String.class,destinationPort);

22 Aralık 2019 Pazar

SpringMVC RestTemplate Sınıfı - Kullanmayın

Giriş
Şu satırı dahil ederiz.
import org.springframework.web.client.RestTemplate;
Bu sınıfın bazı ayarlarını değiştirmek için RestTemplateBuilder kullanılabilir.

WebClient Tercih Edilmeli
Açıklaması şöyle. WebClient nesnesinin kullanılması öneriliyor.
As of 5.0, the non-blocking, reactive org.springframework.web.reactive.client.WebClient offers a modern alternative to the RestTemplate with efficient support for both sync and async, as well as streaming scenarios. The RestTemplate will be deprecated in a future version and will not have major new features added going forward.
Thread Modeli
Açıklaması şöyle
- It is based on a thread per request model, so it can lead to a thread exhaustion state, especially when we deal with the slow third-party APIs.
- It requires additional code for handling error responses.
Converter'lar
Spring bu sınıfa bir sürü converter nesnesini kendiliğinde ekliyor. Açıklaması şöyle.
If you start a Spring application, it takes most configuration to itself, including adding converters (default converters from package org.springframework.http.converter.*: ByteArrayHttpMessageConverter, StringHttpMessageConverter, ResourceHttpMessageConverter, AllEncompassingHttpMessageConverter, MappingJackson2XmlHttpMessageConverter, MappingJackson2HttpMessageConverter).
MappingJackson2HttpMessageConverter sınıfı yazısına bakabilirsiniz.

Kullanım
1.getForEntity(), postForEntity() metodları girdi olarak HttpEntity alır, ResponseEntity verir.

2.getForObject(), postForObject() metodları girdi olarak bizim belirttiğimiz nesneyi alır ve yine bizim belirttiğimiz nesne tipini verir.

3. execute() en genel metoddur ve tam kontrol verir. exchange() ise getForEntity() ve postForEntity() metodlarına göre biraz daha fazla kontrol verir.

ForObject vs ForEntity
Açıklaması şöyle
You may have seen that there are two types of methods which are named slightly different. As an example I can say: getForObject and getForEntity…What is the difference? Let’s see method definitions.

<T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)

Look at the return types!

  • First method returns an object which is having the type of response mapped class. We can access that object’s data if we need them for next logic. That’s all.

  • But in the second method, we get the object wrapped inside a ResponseEntity. There we have more details. Since it is a ResponseEntity, we can access API details like status / status code / headers. This is better when we need to check status and execute the next logic. Ex: — if and only if payment request gives status code 200, send the order response back. We need to call getBody() method to get the exact response data from of the wrapper.


constructor
Şöyle yaparız.
RestTemplate restTemplate = new RestTemplate();
constructor - SimpleClientHttpRequestFactory
SimpleClientHttpRequestFactory yazısına taşıdım.

constructor - HttpComponentsClientHttpRequestFactory 
HttpComponentsClientHttpRequestFactory yazısına taşıdım.

exchange metodu - RequestEntity
exchange() metodu yazısına taşıdım.

execute metodu - URI + HttpMethod + RequestCallback + ResponseCallback
İmzası şöyle.
execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
        @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables)
Açıklaması şöyle. Mümkünse getForObject(), postForObject() tercih edilmeli.
The most generalized way to execute a request, with full control over the preparation of the request and retrieving the response through callback interfaces.
exchange metodu ile farkının açıklaması şöyle. Bu metod ile Bir HttpEntity gönderme ve istenilen Class tipinden sonuç olma imkanı yok! Her şeyi elle yapmak lazım.

Örnek
Şöyle yaparız.
this.restTemplate.execute(uriToCall, HttpMethod.GET, null, clientHttpResponse -> {
    File file= File.createTempFile("download", "tmp");
    StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(file));
});
Örnek
Şöyle yaparız.
RestTemplate restTemplate // = ...;

RequestCallback requestCallback = request -> request.getHeaders()
        .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));

// Streams the response
ResponseExtractor<Void> responseExtractor = response -> {
    // Here I write the response to a file but do what you like
    Path path = Paths.get("http://some/path");
    Files.copy(response.getBody(), path);
    return null;
};
restTemplate.execute(URI.create("www.something.com"), HttpMethod.GET, requestCallback,
  responseExtractor);
getForEntity metodu - URI + Class
Sonuç ResponseEntity<T> tipindendir. T tipi ikinci parametredir. Mümkünse getForObject() metodu tercih edilmeli.

Örnek
Şöyle yaparız.
ResponseEntity<Resource> response = this.restTemplate.getForEntity(uriToCall,
  Resource.class);
Örnek
Şöyle yaparız.
URI uri = ...;
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
Döndürülen response nesnesinin içindeki string'e getBody() çağrısı ile erişiriz. Veri Json olsun. Kullanmak için şöyle yaparız.
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(response.getBody());

JsonNode items = root.get("items");
if (items != null && items.size() > 0) {

  for (int i = 0; i < items.size(); i++) {
    JsonNode channelNode = items.get(i);
    channelList.add(channelNode.get("id").asText());
  }
}
getForObject metodu
getForObject metodu yazısına taşıdım

getInterceptors metodu
Şöyle yaparız.
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
Örnek
Şöyle yaparız
BasicAuthorizationInterceptor interceptor = 
  new BasicAuthorizationInterceptor(username, password)
rest.getInterceptors().add(interceptor);
getMessageConverters metodu
Örnek
Açıklaması şöyle.
Note that you should add it to the beginning of the list so that it takes precedence over the default MappingJackson2HttpMessageConverter that Spring has already registered.
Şöyle yaparız.
restTemplate.getMessageConverters().add(0,
  new StringHttpMessageConverter(Charset.forName("utf-8")));
getRequestFactory metodu
Şöyle yaparız.
HttpComponentsClientHttpRequestFactory rf =
  (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();
postForEntity metodu
postForEntity metodu yazısına taşıdım.

postForObject metodu
postForObject metodu yazısına taşıdım.

setErrorHandler metodu
Şöyle yaparız.
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
  @Override public boolean hasError(ClientHttpResponse response)
    throws IOException {
    try {
      //Do your stuff
      return super.hasError(response);
    } catch (Exception e) {
      ...;
       return true;
    }
  }

  @Override public void handleError(ClientHttpResponse response)
    throws IOException {
    try {
      //Do your stuff
      super.handleError(response);
    } catch (Exception e) {
      ...
      throw e;
    }
  }
});
setInterceptors metodu
setInterceptors metodu yazısına taşıdım

setUriTemplateHandler metodu
base uri tanımlamaya yarar
Örnek
Şöyle yaparız
import org.springframework.boot.web.client.RootUriTemplateHandler;

String baseUri = ...;
restTemplate.setUriTemplateHandler(new RootUriTemplateHandler(baseUri));