Skip to content

Instantly share code, notes, and snippets.

@pavelfomin
Last active April 19, 2024 15:09
Show Gist options
  • Save pavelfomin/9906ea08e238ba40ff1f1197925ade57 to your computer and use it in GitHub Desktop.
Save pavelfomin/9906ea08e238ba40ff1f1197925ade57 to your computer and use it in GitHub Desktop.
ControllerExceptionHandler that uses existing org.springframework.http.ProblemDetail for custom exception handling
import com.google.api.client.http.HttpHeaders
import com.google.api.client.http.HttpResponseException
import com.google.cloud.storage.StorageException
import org.spockframework.spring.SpringBean
import org.springframework.test.web.servlet.ResultActions
import static org.hamcrest.Matchers.is
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
class CloudStorageControllerSpec extends BaseControllerISpec {
static final MediaType PROBLEM_JSON_MEDIA_TYPE = new MediaType(
MediaType.APPLICATION_PROBLEM_JSON.getType(), MediaType.APPLICATION_PROBLEM_JSON.getSubtype()
)
@SpringBean
CloudStorageService cloudStorageService = Mock()
void setup() {
0 * _
}
def "get storage content by bucket - storage error"() {
given:
String bucket = "bucket"
StorageException exception = new StorageException(404, "not found")
when:
ResultActions resultActions = mockMvc.perform(
get("/storage/{bucket}", bucket)
.accept(JSON_MEDIA_TYPE)
)
then: "exception with a valid HTTP code is thrown"
1 * cloudStorageService.list(bucket) >> { throw exception }
resultActions
.andDo(print())
.andExpect(status().isNotFound())
.andExpect(content().contentType(PROBLEM_JSON_MEDIA_TYPE))
.andExpect(jsonPath('$.title', is("Not Found")))
.andExpect(jsonPath('$.detail', is(exception.message)))
.andExpect(jsonPath('$.status', is(exception.code)))
when:
exception = new StorageException(0, "unknown")
resultActions = mockMvc.perform(
get("/storage/{bucket}", bucket)
.accept(JSON_MEDIA_TYPE)
)
then: "exception with an invalid HTTP code is thrown"
1 * cloudStorageService.list(bucket) >> { throw exception }
resultActions
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(content().contentType(PROBLEM_JSON_MEDIA_TYPE))
.andExpect(jsonPath('$.title', is("Internal Server Error")))
.andExpect(jsonPath('$.detail', is(exception.message)))
.andExpect(jsonPath('$.status', is(500)))
}
}
import com.google.api.client.http.HttpResponseException;
import com.google.cloud.BaseServiceException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class ControllerExceptionHandler {
@ExceptionHandler
ResponseEntity<?> handleCloudStorageException(BaseServiceException e) {
return handleException(HttpStatus.resolve(e.getCode()), e);
}
@ExceptionHandler
ResponseEntity<?> handleHttpResponseException(HttpResponseException e) {
return handleException(HttpStatus.resolve(e.getStatusCode()), e);
}
ResponseEntity<?> handleException(HttpStatus status, Exception e) {
if (status == null) {
status = HttpStatus.INTERNAL_SERVER_ERROR;
}
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(status, e.getMessage());
return new ResponseEntity<>(problemDetail, status);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment