From 23c2c9d516884fcc569cc0ad451214e6eff1fc09 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Thu, 7 Dec 2023 16:40:26 -0600 Subject: [PATCH] feat(web): Improve error handling for WebClient This ensures that errors in WebClient are treated similarly to Retrofit. --- .../exceptions/GenericExceptionHandlers.java | 35 ++++++++++++++++++ .../web/exceptions/UpstreamRequestError.java | 36 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 kork-web/src/main/java/com/netflix/spinnaker/kork/web/exceptions/UpstreamRequestError.java diff --git a/kork-web/src/main/java/com/netflix/spinnaker/kork/web/exceptions/GenericExceptionHandlers.java b/kork-web/src/main/java/com/netflix/spinnaker/kork/web/exceptions/GenericExceptionHandlers.java index 1f9a0cefa..8513840cc 100644 --- a/kork-web/src/main/java/com/netflix/spinnaker/kork/web/exceptions/GenericExceptionHandlers.java +++ b/kork-web/src/main/java/com/netflix/spinnaker/kork/web/exceptions/GenericExceptionHandlers.java @@ -30,11 +30,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.security.access.AccessDeniedException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.reactive.function.client.WebClientResponseException; import retrofit.RetrofitError; import retrofit.client.Header; @@ -89,6 +94,36 @@ public void handleIllegalStateException( handleResponseStatusAnnotatedException(e, response); } + @ExceptionHandler(WebClientResponseException.class) + public ResponseEntity handleWebClientResponseException( + WebClientResponseException e) { + HttpStatus statusCode = e.getStatusCode(); + var builder = ResponseEntity.status(statusCode); + var error = new UpstreamRequestError(); + error.setStatus(e.getRawStatusCode()); + error.setError(e.getStatusText()); + HttpRequest request = e.getRequest(); + if (request != null) { + error.setUri(request.getURI()); + error.setMethod(request.getMethod()); + } + HttpHeaders headers = e.getHeaders(); + String eTag = headers.getETag(); + if (eTag != null) { + builder.eTag(eTag); + } + long lastModified = headers.getLastModified(); + if (lastModified != -1) { + builder.lastModified(lastModified); + } + MediaType contentType = headers.getContentType(); + if (contentType != null && contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) { + error.setBody(e.getResponseBodyAsString()); + } + error.setMessage(exceptionMessageDecorator.decorate(e, e.getMessage())); + return builder.body(error); + } + @ExceptionHandler(RetrofitError.class) public void handleRetrofitError( RetrofitError e, HttpServletResponse response, HttpServletRequest request) diff --git a/kork-web/src/main/java/com/netflix/spinnaker/kork/web/exceptions/UpstreamRequestError.java b/kork-web/src/main/java/com/netflix/spinnaker/kork/web/exceptions/UpstreamRequestError.java new file mode 100644 index 000000000..4a37c4b3f --- /dev/null +++ b/kork-web/src/main/java/com/netflix/spinnaker/kork/web/exceptions/UpstreamRequestError.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Apple Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.kork.web.exceptions; + +import com.fasterxml.jackson.annotation.JsonRawValue; +import java.net.URI; +import java.time.Instant; +import lombok.Getter; +import lombok.Setter; +import org.springframework.http.HttpMethod; + +@Getter +@Setter +public class UpstreamRequestError { + private int status; + private String error; + private Instant timestamp = Instant.now(); + private HttpMethod method; + private URI uri; + private String message; + @JsonRawValue private String body; +}