下面列出了怎么用org.springframework.core.codec.DecodingException的API类实例代码及写法,或者点击链接到github查看源代码。
@SuppressWarnings("unchecked")
@Override
public Mono<Void> write(Publisher<? extends Message> inputStream, ResolvableType elementType,
@Nullable MediaType mediaType, ReactiveHttpOutputMessage message, Map<String, Object> hints) {
try {
Message.Builder builder = getMessageBuilder(elementType.toClass());
Descriptors.Descriptor descriptor = builder.getDescriptorForType();
message.getHeaders().add(X_PROTOBUF_SCHEMA_HEADER, descriptor.getFile().getName());
message.getHeaders().add(X_PROTOBUF_MESSAGE_HEADER, descriptor.getFullName());
if (inputStream instanceof Flux) {
if (mediaType == null) {
message.getHeaders().setContentType(((HttpMessageEncoder<?>)getEncoder()).getStreamingMediaTypes().get(0));
}
else if (!ProtobufEncoder.DELIMITED_VALUE.equals(mediaType.getParameters().get(ProtobufEncoder.DELIMITED_KEY))) {
Map<String, String> parameters = new HashMap<>(mediaType.getParameters());
parameters.put(ProtobufEncoder.DELIMITED_KEY, ProtobufEncoder.DELIMITED_VALUE);
message.getHeaders().setContentType(new MediaType(mediaType.getType(), mediaType.getSubtype(), parameters));
}
}
return super.write(inputStream, elementType, mediaType, message, hints);
}
catch (Exception ex) {
return Mono.error(new DecodingException("Could not read Protobuf message: " + ex.getMessage(), ex));
}
}
@Override
public Object decode(DataBuffer dataBuffer, ResolvableType targetType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) throws DecodingException {
try {
ObjectReader objectReader = getObjectReader(targetType, hints);
Object value = objectReader.readValue(dataBuffer.asInputStream());
logValue(value, hints);
return value;
}
catch (IOException ex) {
throw processException(ex);
}
finally {
DataBufferUtils.release(dataBuffer);
}
}
@Override
@SuppressWarnings({"rawtypes", "unchecked", "cast"}) // XMLEventReader is Iterator<Object> on JDK 9
public Object decode(DataBuffer dataBuffer, ResolvableType targetType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) throws DecodingException {
try {
Iterator eventReader = inputFactory.createXMLEventReader(dataBuffer.asInputStream());
List<XMLEvent> events = new ArrayList<>();
eventReader.forEachRemaining(event -> events.add((XMLEvent) event));
return unmarshal(events, targetType.toClass());
}
catch (XMLStreamException ex) {
throw Exceptions.propagate(ex);
}
finally {
DataBufferUtils.release(dataBuffer);
}
}
@SuppressWarnings("unchecked")
@Override
public Mono<Void> write(Publisher<? extends Message> inputStream, ResolvableType elementType,
@Nullable MediaType mediaType, ReactiveHttpOutputMessage message, Map<String, Object> hints) {
try {
Message.Builder builder = getMessageBuilder(elementType.toClass());
Descriptors.Descriptor descriptor = builder.getDescriptorForType();
message.getHeaders().add(X_PROTOBUF_SCHEMA_HEADER, descriptor.getFile().getName());
message.getHeaders().add(X_PROTOBUF_MESSAGE_HEADER, descriptor.getFullName());
if (inputStream instanceof Flux) {
if (mediaType == null) {
message.getHeaders().setContentType(((HttpMessageEncoder<?>)getEncoder()).getStreamingMediaTypes().get(0));
}
else if (!ProtobufEncoder.DELIMITED_VALUE.equals(mediaType.getParameters().get(ProtobufEncoder.DELIMITED_KEY))) {
Map<String, String> parameters = new HashMap<>(mediaType.getParameters());
parameters.put(ProtobufEncoder.DELIMITED_KEY, ProtobufEncoder.DELIMITED_VALUE);
message.getHeaders().setContentType(new MediaType(mediaType.getType(), mediaType.getSubtype(), parameters));
}
}
return super.write(inputStream, elementType, mediaType, message, hints);
}
catch (Exception ex) {
return Mono.error(new DecodingException("Could not read Protobuf message: " + ex.getMessage(), ex));
}
}
private CodecException processException(IOException ex) {
if (ex instanceof InvalidDefinitionException) {
JavaType type = ((InvalidDefinitionException) ex).getType();
return new CodecException("Type definition error: " + type, ex);
}
if (ex instanceof JsonProcessingException) {
String originalMessage = ((JsonProcessingException) ex).getOriginalMessage();
return new DecodingException("JSON decoding error: " + originalMessage, ex);
}
return new DecodingException("I/O error while parsing input stream", ex);
}
@Test
public void exceedMaxSize() {
this.decoder.setMaxMessageSize(1);
Mono<DataBuffer> input = dataBuffer(this.testMsg1);
testDecode(input, Msg.class, step -> step
.verifyError(DecodingException.class));
}
@Test
public void invalidData() {
Flux<DataBuffer> input =
Flux.from(stringBuffer("{\"foofoo\": \"foofoo\", \"barbar\": \"barbar\""));
testDecode(input, Pojo.class, step -> step
.verifyError(DecodingException.class));
}
@Test // SPR-16521
public void jsonEOFExceptionIsWrappedAsDecodingError() {
Flux<DataBuffer> source = Flux.just(stringBuffer("{\"status\": \"noClosingQuote}"));
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, false);
StepVerifier.create(tokens)
.expectError(DecodingException.class)
.verify();
}
@Test
public void exceedMaxSize() {
this.decoder.setMaxMessageSize(1);
Mono<DataBuffer> input = dataBuffer(this.testMsg1);
testDecode(input, Msg.class, step -> step
.verifyError(DecodingException.class));
}
@Test
public void invalidData() {
Flux<DataBuffer> input =
Flux.from(stringBuffer("{\"foofoo\": \"foofoo\", \"barbar\": \"barbar\""));
testDecode(input, Pojo.class, step -> step
.verifyError(DecodingException.class));
}
@Test // SPR-16521
public void jsonEOFExceptionIsWrappedAsDecodingError() {
Flux<DataBuffer> source = Flux.just(stringBuffer("{\"status\": \"noClosingQuote}"));
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(source, this.jsonFactory, false);
StepVerifier.create(tokens)
.expectError(DecodingException.class)
.verify();
}
@Test
public void verify_MALFORMED_REQUEST_is_returned_if_passed_bad_json_body_which_results_in_DecodingException_cause() throws IOException {
SampleModel originalValidPayloadObj = randomizedSampleModel();
String originalValidPayloadAsString = objectMapper.writeValueAsString(originalValidPayloadObj);
@SuppressWarnings("unchecked")
Map<String, Object> badRequestPayloadAsMap = objectMapper.readValue(originalValidPayloadAsString, Map.class);
badRequestPayloadAsMap.put("throw_manual_error", "not-a-boolean");
String badJsonPayloadAsString = objectMapper.writeValueAsString(badRequestPayloadAsMap);
ExtractableResponse response =
given()
.baseUri("http://localhost")
.port(SERVER_PORT)
.contentType(ContentType.JSON)
.log().all()
.when()
.body(badJsonPayloadAsString)
.post(POST_SAMPLE_MODEL_ENDPOINT_WITH_JSR_303_VALIDATION)
.then()
.log().all()
.extract();
verifyErrorReceived(response, SampleCoreApiError.MALFORMED_REQUEST);
ServerWebInputException ex = verifyResponseStatusExceptionSeenByBackstopper(
ServerWebInputException.class, 400
);
verifyExceptionHasCauseOfType(ex, DecodingException.class);
verifyHandlingResult(
SampleCoreApiError.MALFORMED_REQUEST,
Pair.of("exception_message", quotesToApostrophes(ex.getMessage())),
Pair.of("method_parameter", ex.getMethodParameter().toString())
);
}
@DataProvider(value = {
"400 | MALFORMED_REQUEST",
"401 | UNAUTHORIZED"
}, splitBy = "\\|")
@Test
public void handleFluxExceptions_returns_MALFORMED_REQUEST_for_ResponseStatusException_with_DecodingException_cause_only_if_status_is_400(
int statusCode, BarebonesCoreApiErrorForTesting expectedError
) {
// given
ResponseStatusException ex = new ResponseStatusException(
HttpStatus.resolve(statusCode),
"Some ResponseStatusException reason",
new DecodingException("Some decoding ex")
);
List<Pair<String, String>> expectedExtraDetailsForLogging = new ArrayList<>();
ApiExceptionHandlerUtils.DEFAULT_IMPL.addBaseExceptionMessageToExtraDetailsForLogging(
ex, expectedExtraDetailsForLogging
);
// when
ApiExceptionHandlerListenerResult result = listener.handleSpringMvcOrWebfluxSpecificFrameworkExceptions(ex);
// then
validateResponse(
result,
true,
singleton(expectedError),
expectedExtraDetailsForLogging
);
}
private Throwable handleReadError(MethodParameter parameter, Message<?> message, Throwable ex) {
return ex instanceof DecodingException ?
new MethodArgumentResolutionException(message, parameter, "Failed to read HTTP message", ex) : ex;
}
@Override
public <T> Mono<T> bodyToMono(Class<? extends T> elementClass) {
Mono<T> mono = body(BodyExtractors.toMono(elementClass));
return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER)
.onErrorMap(DecodingException.class, DECODING_MAPPER);
}
@Override
public <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> typeReference) {
Mono<T> mono = body(BodyExtractors.toMono(typeReference));
return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER)
.onErrorMap(DecodingException.class, DECODING_MAPPER);
}
@Override
public <T> Flux<T> bodyToFlux(Class<? extends T> elementClass) {
Flux<T> flux = body(BodyExtractors.toFlux(elementClass));
return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER)
.onErrorMap(DecodingException.class, DECODING_MAPPER);
}
@Override
public <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> typeReference) {
Flux<T> flux = body(BodyExtractors.toFlux(typeReference));
return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER)
.onErrorMap(DecodingException.class, DECODING_MAPPER);
}
private Throwable handleReadError(MethodParameter parameter, Throwable ex) {
return (ex instanceof DecodingException ?
new ServerWebInputException("Failed to read HTTP message", parameter, ex) : ex);
}
@Override
public <T> Mono<T> bodyToMono(Class<? extends T> elementClass) {
Mono<T> mono = body(BodyExtractors.toMono(elementClass));
return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER)
.onErrorMap(DecodingException.class, DECODING_MAPPER);
}
@Override
public <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> typeReference) {
Mono<T> mono = body(BodyExtractors.toMono(typeReference));
return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER)
.onErrorMap(DecodingException.class, DECODING_MAPPER);
}
@Override
public <T> Flux<T> bodyToFlux(Class<? extends T> elementClass) {
Flux<T> flux = body(BodyExtractors.toFlux(elementClass));
return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER)
.onErrorMap(DecodingException.class, DECODING_MAPPER);
}
@Override
public <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> typeReference) {
Flux<T> flux = body(BodyExtractors.toFlux(typeReference));
return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER)
.onErrorMap(DecodingException.class, DECODING_MAPPER);
}
private Throwable handleReadError(MethodParameter parameter, Throwable ex) {
return (ex instanceof DecodingException ?
new ServerWebInputException("Failed to read HTTP message", parameter, ex) : ex);
}
@Override
public Forwarding decode(DataBuffer buffer, ResolvableType targetType,
MimeType mimeType, Map<String, Object> hints) throws DecodingException {
ByteBuf byteBuf = TagsMetadata.asByteBuf(buffer);
return Forwarding.decodeForwarding(byteBuf);
}
@Override
public RouteSetup decode(DataBuffer buffer, ResolvableType targetType,
MimeType mimeType, Map<String, Object> hints) throws DecodingException {
ByteBuf byteBuf = TagsMetadata.asByteBuf(buffer);
return RouteSetup.decodeRouteSetup(byteBuf);
}
protected @NotNull ApiExceptionHandlerListenerResult handleResponseStatusException(
@NotNull ResponseStatusException ex
) {
// ResponseStatusException is technically in spring-web, so it could be handled in backstopper-spring-web's
// OneOffSpringCommonFrameworkExceptionHandlerListener, except that it's also spring 5 so we'd have to
// have yet another module for backstopper-spring-web5. Or we'd have to do a bunch of obnoxious reflection.
// Since Spring WebFlux seems to be the only place ResponseStatusException is used, we'll just shove this
// logic here for now. It can be moved later if needed.
int statusCode = ex.getStatus().value();
List<Pair<String, String>> extraDetailsForLogging = new ArrayList<>();
utils.addBaseExceptionMessageToExtraDetailsForLogging(ex, extraDetailsForLogging);
addExtraDetailsForLoggingForResponseStatusException(ex, extraDetailsForLogging);
// Search for a more specific way to handle this based on the cause.
Throwable exCause = ex.getCause();
if (exCause instanceof TypeMismatchException) {
// If the cause is a TypeMismatchException and status code is acceptable, then we can have the
// handleTypeMismatchException(...) method deal with it for a more specific response.
// For safety make sure the status code is one we expect.
TypeMismatchException tmeCause = (TypeMismatchException) ex.getCause();
int expectedStatusCode = (tmeCause instanceof ConversionNotSupportedException) ? 500 : 400;
if (statusCode == expectedStatusCode) {
// The specific cause exception type and the status code match,
// so we can use handleTypeMismatchException(...).
return handleTypeMismatchException(tmeCause, extraDetailsForLogging, false);
}
}
else if (exCause instanceof DecodingException && statusCode == 400) {
return handleError(projectApiErrors.getMalformedRequestApiError(), extraDetailsForLogging);
}
// Exception cause didn't help. Try parsing the reason message.
String exReason = (ex.getReason() == null) ? "" : ex.getReason();
String[] exReasonWords = exReason.split(" ");
RequiredParamData missingRequiredParam = parseExReasonForMissingRequiredParam(exReasonWords, exReason);
if (missingRequiredParam != null && statusCode == 400) {
return handleError(
new ApiErrorWithMetadata(
projectApiErrors.getMalformedRequestApiError(),
Pair.of("missing_param_name", missingRequiredParam.paramName),
Pair.of("missing_param_type", missingRequiredParam.paramType)
),
extraDetailsForLogging
);
}
else if (exReason.startsWith("Request body is missing") && statusCode == 400) {
return handleError(projectApiErrors.getMissingExpectedContentApiError(), extraDetailsForLogging);
}
// For any other ResponseStatusException we'll search for an appropriate ApiError by status code.
return handleError(
determineApiErrorToUseForGenericResponseStatusCode(statusCode),
extraDetailsForLogging
);
}
private Throwable handleReadError(MethodParameter parameter, Throwable ex) {
return (ex instanceof DecodingException ? new ServerWebInputException(
"Failed to read HTTP message", parameter, ex) : ex);
}