Browse Source

HttpMessageNotReadableException provides access to HttpInputMessage

Issue: SPR-15588
pull/1870/head
Juergen Hoeller 6 years ago
parent
commit
83faee67d5
  1. 5
      spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java
  2. 48
      spring-web/src/main/java/org/springframework/http/converter/HttpMessageNotReadableException.java
  3. 3
      spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java
  4. 2
      spring-web/src/main/java/org/springframework/http/converter/ResourceHttpMessageConverter.java
  5. 2
      spring-web/src/main/java/org/springframework/http/converter/feed/AbstractWireFeedHttpMessageConverter.java
  6. 2
      spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java
  7. 2
      spring-web/src/main/java/org/springframework/http/converter/json/AbstractJsonHttpMessageConverter.java
  8. 21
      spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java
  9. 35
      spring-web/src/main/java/org/springframework/http/converter/xml/AbstractXmlHttpMessageConverter.java
  10. 12
      spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java
  11. 13
      spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java
  12. 32
      spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java
  13. 30
      spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java
  14. 12
      spring-web/src/test/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverterTests.java
  15. 2
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java
  16. 2
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java

5
spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java

@ -172,7 +172,7 @@ public class BufferedImageHttpMessageConverter implements HttpMessageConverter<B
imageInputStream = createImageInputStream(inputMessage.getBody()); imageInputStream = createImageInputStream(inputMessage.getBody());
MediaType contentType = inputMessage.getHeaders().getContentType(); MediaType contentType = inputMessage.getHeaders().getContentType();
if (contentType == null) { if (contentType == null) {
throw new HttpMessageNotReadableException("No Content-Type header"); throw new HttpMessageNotReadableException("No Content-Type header", inputMessage);
} }
Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByMIMEType(contentType.toString()); Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByMIMEType(contentType.toString());
if (imageReaders.hasNext()) { if (imageReaders.hasNext()) {
@ -184,7 +184,8 @@ public class BufferedImageHttpMessageConverter implements HttpMessageConverter<B
} }
else { else {
throw new HttpMessageNotReadableException( throw new HttpMessageNotReadableException(
"Could not find javax.imageio.ImageReader for Content-Type [" + contentType + "]"); "Could not find javax.imageio.ImageReader for Content-Type [" + contentType + "]",
inputMessage);
} }
} }
finally { finally {

48
spring-web/src/main/java/org/springframework/http/converter/HttpMessageNotReadableException.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,33 +16,79 @@
package org.springframework.http.converter; package org.springframework.http.converter;
import org.springframework.http.HttpInputMessage;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/** /**
* Thrown by {@link HttpMessageConverter} implementations when the * Thrown by {@link HttpMessageConverter} implementations when the
* {@link HttpMessageConverter#read} method fails. * {@link HttpMessageConverter#read} method fails.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0 * @since 3.0
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class HttpMessageNotReadableException extends HttpMessageConversionException { public class HttpMessageNotReadableException extends HttpMessageConversionException {
@Nullable
private final HttpInputMessage httpInputMessage;
/** /**
* Create a new HttpMessageNotReadableException. * Create a new HttpMessageNotReadableException.
* @param msg the detail message * @param msg the detail message
* @deprecated as of 5.1, in favor of {@link #HttpMessageNotReadableException(String, HttpInputMessage)}
*/ */
@Deprecated
public HttpMessageNotReadableException(String msg) { public HttpMessageNotReadableException(String msg) {
super(msg); super(msg);
this.httpInputMessage = null;
} }
/** /**
* Create a new HttpMessageNotReadableException. * Create a new HttpMessageNotReadableException.
* @param msg the detail message * @param msg the detail message
* @param cause the root cause (if any) * @param cause the root cause (if any)
* @deprecated as of 5.1, in favor of {@link #HttpMessageNotReadableException(String, Throwable, HttpInputMessage)}
*/ */
@Deprecated
public HttpMessageNotReadableException(String msg, @Nullable Throwable cause) { public HttpMessageNotReadableException(String msg, @Nullable Throwable cause) {
super(msg, cause); super(msg, cause);
this.httpInputMessage = null;
}
/**
* Create a new HttpMessageNotReadableException.
* @param msg the detail message
* @param httpInputMessage the original HTTP message
* @since 5.1
*/
public HttpMessageNotReadableException(String msg, HttpInputMessage httpInputMessage) {
super(msg);
this.httpInputMessage = httpInputMessage;
}
/**
* Create a new HttpMessageNotReadableException.
* @param msg the detail message
* @param cause the root cause (if any)
* @param httpInputMessage the original HTTP message
* @since 5.1
*/
public HttpMessageNotReadableException(String msg, @Nullable Throwable cause, HttpInputMessage httpInputMessage) {
super(msg, cause);
this.httpInputMessage = httpInputMessage;
}
/**
* Return the original HTTP message.
* @since 5.1
*/
public HttpInputMessage getHttpInputMessage() {
Assert.state(this.httpInputMessage != null, "No HttpInputMessage available - use non-deprecated constructors");
return this.httpInputMessage;
} }
} }

3
spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java

@ -113,7 +113,8 @@ public class ObjectToStringHttpMessageConverter extends AbstractHttpMessageConve
Object result = this.conversionService.convert(value, clazz); Object result = this.conversionService.convert(value, clazz);
if (result == null) { if (result == null) {
throw new HttpMessageNotReadableException( throw new HttpMessageNotReadableException(
"Unexpected null conversion result for '" + value + "' to " + clazz); "Unexpected null conversion result for '" + value + "' to " + clazz,
inputMessage);
} }
return result; return result;
} }

2
spring-web/src/main/java/org/springframework/http/converter/ResourceHttpMessageConverter.java

@ -97,7 +97,7 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
}; };
} }
else { else {
throw new HttpMessageNotReadableException("Unsupported resource class: " + clazz); throw new HttpMessageNotReadableException("Unsupported resource class: " + clazz, inputMessage);
} }
} }

2
spring-web/src/main/java/org/springframework/http/converter/feed/AbstractWireFeedHttpMessageConverter.java

@ -78,7 +78,7 @@ public abstract class AbstractWireFeedHttpMessageConverter<T extends WireFeed>
return (T) feedInput.build(reader); return (T) feedInput.build(reader);
} }
catch (FeedException ex) { catch (FeedException ex) {
throw new HttpMessageNotReadableException("Could not read WireFeed: " + ex.getMessage(), ex); throw new HttpMessageNotReadableException("Could not read WireFeed: " + ex.getMessage(), ex, inputMessage);
} }
} }

2
spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java

@ -242,7 +242,7 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex); throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
} }
catch (JsonProcessingException ex) { catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex); throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);
} }
} }

2
spring-web/src/main/java/org/springframework/http/converter/json/AbstractJsonHttpMessageConverter.java

@ -109,7 +109,7 @@ public abstract class AbstractJsonHttpMessageConverter extends AbstractGenericHt
return readInternal(resolvedType, reader); return readInternal(resolvedType, reader);
} }
catch (Exception ex) { catch (Exception ex) {
throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex); throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex, inputMessage);
} }
} }

21
spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java

@ -273,12 +273,13 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<M
void merge(InputStream input, Charset charset, MediaType contentType, void merge(InputStream input, Charset charset, MediaType contentType,
ExtensionRegistry extensionRegistry, Message.Builder builder) ExtensionRegistry extensionRegistry, Message.Builder builder)
throws IOException, HttpMessageNotReadableException; throws IOException, HttpMessageConversionException;
void print(Message message, OutputStream output, MediaType contentType, Charset charset) void print(Message message, OutputStream output, MediaType contentType, Charset charset)
throws IOException, HttpMessageNotWritableException; throws IOException, HttpMessageConversionException;
} }
/** /**
* {@link ProtobufFormatSupport} implementation used when * {@link ProtobufFormatSupport} implementation used when
* {@code com.googlecode.protobuf.format.FormatFactory} is available. * {@code com.googlecode.protobuf.format.FormatFactory} is available.
@ -311,7 +312,7 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<M
@Override @Override
public void merge(InputStream input, Charset charset, MediaType contentType, public void merge(InputStream input, Charset charset, MediaType contentType,
ExtensionRegistry extensionRegistry, Message.Builder builder) ExtensionRegistry extensionRegistry, Message.Builder builder)
throws IOException, HttpMessageNotReadableException { throws IOException, HttpMessageConversionException {
if (contentType.isCompatibleWith(APPLICATION_JSON)) { if (contentType.isCompatibleWith(APPLICATION_JSON)) {
this.jsonFormatter.merge(input, charset, extensionRegistry, builder); this.jsonFormatter.merge(input, charset, extensionRegistry, builder);
@ -320,14 +321,14 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<M
this.xmlFormatter.merge(input, charset, extensionRegistry, builder); this.xmlFormatter.merge(input, charset, extensionRegistry, builder);
} }
else { else {
throw new HttpMessageNotReadableException( throw new HttpMessageConversionException(
"protobuf-java-format does not support parsing " + contentType); "protobuf-java-format does not support parsing " + contentType);
} }
} }
@Override @Override
public void print(Message message, OutputStream output, MediaType contentType, Charset charset) public void print(Message message, OutputStream output, MediaType contentType, Charset charset)
throws IOException, HttpMessageNotWritableException { throws IOException, HttpMessageConversionException {
if (contentType.isCompatibleWith(APPLICATION_JSON)) { if (contentType.isCompatibleWith(APPLICATION_JSON)) {
this.jsonFormatter.print(message, output, charset); this.jsonFormatter.print(message, output, charset);
@ -339,7 +340,7 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<M
this.htmlFormatter.print(message, output, charset); this.htmlFormatter.print(message, output, charset);
} }
else { else {
throw new HttpMessageNotWritableException( throw new HttpMessageConversionException(
"protobuf-java-format does not support printing " + contentType); "protobuf-java-format does not support printing " + contentType);
} }
} }
@ -374,21 +375,21 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<M
@Override @Override
public void merge(InputStream input, Charset charset, MediaType contentType, public void merge(InputStream input, Charset charset, MediaType contentType,
ExtensionRegistry extensionRegistry, Message.Builder builder) ExtensionRegistry extensionRegistry, Message.Builder builder)
throws IOException, HttpMessageNotReadableException { throws IOException, HttpMessageConversionException {
if (contentType.isCompatibleWith(APPLICATION_JSON)) { if (contentType.isCompatibleWith(APPLICATION_JSON)) {
InputStreamReader reader = new InputStreamReader(input, charset); InputStreamReader reader = new InputStreamReader(input, charset);
this.parser.merge(reader, builder); this.parser.merge(reader, builder);
} }
else { else {
throw new HttpMessageNotReadableException( throw new HttpMessageConversionException(
"protobuf-java-util does not support parsing " + contentType); "protobuf-java-util does not support parsing " + contentType);
} }
} }
@Override @Override
public void print(Message message, OutputStream output, MediaType contentType, Charset charset) public void print(Message message, OutputStream output, MediaType contentType, Charset charset)
throws IOException, HttpMessageNotWritableException { throws IOException, HttpMessageConversionException {
if (contentType.isCompatibleWith(APPLICATION_JSON)) { if (contentType.isCompatibleWith(APPLICATION_JSON)) {
OutputStreamWriter writer = new OutputStreamWriter(output, charset); OutputStreamWriter writer = new OutputStreamWriter(output, charset);
@ -396,7 +397,7 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<M
writer.flush(); writer.flush();
} }
else { else {
throw new HttpMessageNotWritableException( throw new HttpMessageConversionException(
"protobuf-java-util does not support printing " + contentType); "protobuf-java-util does not support printing " + contentType);
} }
} }

35
spring-web/src/main/java/org/springframework/http/converter/xml/AbstractXmlHttpMessageConverter.java

@ -29,6 +29,7 @@ import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage; import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.http.converter.HttpMessageNotWritableException;
@ -41,6 +42,7 @@ import org.springframework.http.converter.HttpMessageNotWritableException;
* supportedMediaTypes} property. * supportedMediaTypes} property.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0 * @since 3.0
* @param <T> the converted object type * @param <T> the converted object type
*/ */
@ -62,14 +64,31 @@ public abstract class AbstractXmlHttpMessageConverter<T> extends AbstractHttpMes
public final T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage) public final T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException { throws IOException, HttpMessageNotReadableException {
return readFromSource(clazz, inputMessage.getHeaders(), new StreamSource(inputMessage.getBody())); try {
return readFromSource(clazz, inputMessage.getHeaders(), new StreamSource(inputMessage.getBody()));
}
catch (IOException | HttpMessageConversionException ex) {
throw ex;
}
catch (Exception ex) {
throw new HttpMessageNotReadableException("Could not unmarshal to [" + clazz + "]: " + ex.getMessage(),
ex, inputMessage);
}
} }
@Override @Override
protected final void writeInternal(T t, HttpOutputMessage outputMessage) protected final void writeInternal(T t, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException { throws IOException, HttpMessageNotWritableException {
writeToResult(t, outputMessage.getHeaders(), new StreamResult(outputMessage.getBody())); try {
writeToResult(t, outputMessage.getHeaders(), new StreamResult(outputMessage.getBody()));
}
catch (IOException | HttpMessageConversionException ex) {
throw ex;
}
catch (Exception ex) {
throw new HttpMessageNotWritableException("Could not marshal [" + t + "]: " + ex.getMessage(), ex);
}
} }
/** /**
@ -89,21 +108,17 @@ public abstract class AbstractXmlHttpMessageConverter<T> extends AbstractHttpMes
* @param headers the HTTP input headers * @param headers the HTTP input headers
* @param source the HTTP input body * @param source the HTTP input body
* @return the converted object * @return the converted object
* @throws IOException in case of I/O errors * @throws Exception in case of I/O or conversion errors
* @throws HttpMessageNotReadableException in case of conversion errors
*/ */
protected abstract T readFromSource(Class<? extends T> clazz, HttpHeaders headers, Source source) protected abstract T readFromSource(Class<? extends T> clazz, HttpHeaders headers, Source source) throws Exception;
throws IOException, HttpMessageNotReadableException;
/** /**
* Abstract template method called from {@link #writeInternal(Object, HttpOutputMessage)}. * Abstract template method called from {@link #writeInternal(Object, HttpOutputMessage)}.
* @param t the object to write to the output message * @param t the object to write to the output message
* @param headers the HTTP output headers * @param headers the HTTP output headers
* @param result the HTTP output body * @param result the HTTP output body
* @throws IOException in case of I/O errors * @throws Exception in case of I/O or conversion errors
* @throws HttpMessageNotWritableException in case of conversion errors
*/ */
protected abstract void writeToResult(T t, HttpHeaders headers, Result result) protected abstract void writeToResult(T t, HttpHeaders headers, Result result) throws Exception;
throws IOException, HttpMessageNotWritableException;
} }

12
spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java

@ -132,7 +132,7 @@ public class Jaxb2CollectionHttpMessageConverter<T extends Collection>
} }
@Override @Override
protected T readFromSource(Class<? extends T> clazz, HttpHeaders headers, Source source) throws IOException { protected T readFromSource(Class<? extends T> clazz, HttpHeaders headers, Source source) throws Exception {
// should not be called, since we return false for canRead(Class) // should not be called, since we return false for canRead(Class)
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -160,18 +160,20 @@ public class Jaxb2CollectionHttpMessageConverter<T extends Collection>
} }
else { else {
// should not happen, since we check in canRead(Type) // should not happen, since we check in canRead(Type)
throw new HttpMessageNotReadableException("Cannot unmarshal to [" + elementClass + "]"); throw new HttpMessageNotReadableException(
"Cannot unmarshal to [" + elementClass + "]", inputMessage);
} }
event = moveToNextElement(streamReader); event = moveToNextElement(streamReader);
} }
return result; return result;
} }
catch (XMLStreamException ex) { catch (XMLStreamException ex) {
throw new HttpMessageNotReadableException("Failed to read XML stream: " + ex.getMessage(), ex); throw new HttpMessageNotReadableException(
"Failed to read XML stream: " + ex.getMessage(), ex, inputMessage);
} }
catch (UnmarshalException ex) { catch (UnmarshalException ex) {
throw new HttpMessageNotReadableException( throw new HttpMessageNotReadableException(
"Could not unmarshal to [" + elementClass + "]: " + ex.getMessage(), ex); "Could not unmarshal to [" + elementClass + "]: " + ex.getMessage(), ex, inputMessage);
} }
catch (JAXBException ex) { catch (JAXBException ex) {
throw new HttpMessageConversionException("Invalid JAXB setup: " + ex.getMessage(), ex); throw new HttpMessageConversionException("Invalid JAXB setup: " + ex.getMessage(), ex);
@ -237,7 +239,7 @@ public class Jaxb2CollectionHttpMessageConverter<T extends Collection>
} }
@Override @Override
protected void writeToResult(T t, HttpHeaders headers, Result result) throws IOException { protected void writeToResult(T t, HttpHeaders headers, Result result) throws Exception {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

13
spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java

@ -16,7 +16,6 @@
package org.springframework.http.converter.xml; package org.springframework.http.converter.xml;
import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
@ -41,8 +40,6 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConversionException; import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -124,7 +121,7 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
} }
@Override @Override
protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws IOException { protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws Exception {
try { try {
source = processSource(source); source = processSource(source);
Unmarshaller unmarshaller = createUnmarshaller(clazz); Unmarshaller unmarshaller = createUnmarshaller(clazz);
@ -138,13 +135,13 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
} }
catch (NullPointerException ex) { catch (NullPointerException ex) {
if (!isSupportDtd()) { if (!isSupportDtd()) {
throw new HttpMessageNotReadableException("NPE while unmarshalling. " + throw new IllegalStateException("NPE while unmarshalling. " +
"This can happen due to the presence of DTD declarations which are disabled.", ex); "This can happen due to the presence of DTD declarations which are disabled.", ex);
} }
throw ex; throw ex;
} }
catch (UnmarshalException ex) { catch (UnmarshalException ex) {
throw new HttpMessageNotReadableException("Could not unmarshal to [" + clazz + "]: " + ex.getMessage(), ex); throw ex;
} }
catch (JAXBException ex) { catch (JAXBException ex) {
throw new HttpMessageConversionException("Invalid JAXB setup: " + ex.getMessage(), ex); throw new HttpMessageConversionException("Invalid JAXB setup: " + ex.getMessage(), ex);
@ -177,7 +174,7 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
} }
@Override @Override
protected void writeToResult(Object o, HttpHeaders headers, Result result) throws IOException { protected void writeToResult(Object o, HttpHeaders headers, Result result) throws Exception {
try { try {
Class<?> clazz = ClassUtils.getUserClass(o); Class<?> clazz = ClassUtils.getUserClass(o);
Marshaller marshaller = createMarshaller(clazz); Marshaller marshaller = createMarshaller(clazz);
@ -185,7 +182,7 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
marshaller.marshal(o, result); marshaller.marshal(o, result);
} }
catch (MarshalException ex) { catch (MarshalException ex) {
throw new HttpMessageNotWritableException("Could not marshal [" + o + "]: " + ex.getMessage(), ex); throw ex;
} }
catch (JAXBException ex) { catch (JAXBException ex) {
throw new HttpMessageConversionException("Invalid JAXB setup: " + ex.getMessage(), ex); throw new HttpMessageConversionException("Invalid JAXB setup: " + ex.getMessage(), ex);

32
spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,20 +16,15 @@
package org.springframework.http.converter.xml; package org.springframework.http.converter.xml;
import java.io.IOException;
import javax.xml.transform.Result; import javax.xml.transform.Result;
import javax.xml.transform.Source; import javax.xml.transform.Source;
import org.springframework.beans.TypeMismatchException; import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.oxm.Marshaller; import org.springframework.oxm.Marshaller;
import org.springframework.oxm.MarshallingFailureException;
import org.springframework.oxm.Unmarshaller; import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.UnmarshallingFailureException;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -125,28 +120,19 @@ public class MarshallingHttpMessageConverter extends AbstractXmlHttpMessageConve
} }
@Override @Override
protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws IOException { protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws Exception {
Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required"); Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required");
try { Object result = this.unmarshaller.unmarshal(source);
Object result = this.unmarshaller.unmarshal(source); if (!clazz.isInstance(result)) {
if (!clazz.isInstance(result)) { throw new TypeMismatchException(result, clazz);
throw new TypeMismatchException(result, clazz);
}
return result;
}
catch (UnmarshallingFailureException ex) {
throw new HttpMessageNotReadableException("Could not read [" + clazz + "]", ex);
} }
return result;
} }
@Override @Override
protected void writeToResult(Object o, HttpHeaders headers, Result result) throws IOException { protected void writeToResult(Object o, HttpHeaders headers, Result result) throws Exception {
Assert.notNull(this.marshaller, "Property 'marshaller' is required"); Assert.notNull(this.marshaller, "Property 'marshaller' is required");
try { this.marshaller.marshal(o, result);
this.marshaller.marshal(o, result);
}
catch (MarshallingFailureException ex) {
throw new HttpMessageNotWritableException("Could not write [" + o + "]", ex);
}
} }
} }

30
spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java

@ -147,24 +147,24 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
InputStream body = inputMessage.getBody(); InputStream body = inputMessage.getBody();
if (DOMSource.class == clazz) { if (DOMSource.class == clazz) {
return (T) readDOMSource(body); return (T) readDOMSource(body, inputMessage);
} }
else if (SAXSource.class == clazz) { else if (SAXSource.class == clazz) {
return (T) readSAXSource(body); return (T) readSAXSource(body, inputMessage);
} }
else if (StAXSource.class == clazz) { else if (StAXSource.class == clazz) {
return (T) readStAXSource(body); return (T) readStAXSource(body, inputMessage);
} }
else if (StreamSource.class == clazz || Source.class == clazz) { else if (StreamSource.class == clazz || Source.class == clazz) {
return (T) readStreamSource(body); return (T) readStreamSource(body);
} }
else { else {
throw new HttpMessageNotReadableException("Could not read class [" + clazz + throw new HttpMessageNotReadableException("Could not read class [" + clazz +
"]. Only DOMSource, SAXSource, StAXSource, and StreamSource are supported."); "]. Only DOMSource, SAXSource, StAXSource, and StreamSource are supported.", inputMessage);
} }
} }
private DOMSource readDOMSource(InputStream body) throws IOException { private DOMSource readDOMSource(InputStream body, HttpInputMessage inputMessage) throws IOException {
try { try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true); documentBuilderFactory.setNamespaceAware(true);
@ -181,21 +181,23 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
} }
catch (NullPointerException ex) { catch (NullPointerException ex) {
if (!isSupportDtd()) { if (!isSupportDtd()) {
throw new HttpMessageNotReadableException("NPE while unmarshalling: " + throw new HttpMessageNotReadableException("NPE while unmarshalling: This can happen " +
"This can happen due to the presence of DTD declarations which are disabled.", ex); "due to the presence of DTD declarations which are disabled.", ex, inputMessage);
} }
throw ex; throw ex;
} }
catch (ParserConfigurationException ex) { catch (ParserConfigurationException ex) {
throw new HttpMessageNotReadableException("Could not set feature: " + ex.getMessage(), ex); throw new HttpMessageNotReadableException(
"Could not set feature: " + ex.getMessage(), ex, inputMessage);
} }
catch (SAXException ex) { catch (SAXException ex) {
throw new HttpMessageNotReadableException("Could not parse document: " + ex.getMessage(), ex); throw new HttpMessageNotReadableException(
"Could not parse document: " + ex.getMessage(), ex, inputMessage);
} }
} }
@SuppressWarnings("deprecation") // on JDK 9 @SuppressWarnings("deprecation") // on JDK 9
private SAXSource readSAXSource(InputStream body) throws IOException { private SAXSource readSAXSource(InputStream body, HttpInputMessage inputMessage) throws IOException {
try { try {
XMLReader xmlReader = org.xml.sax.helpers.XMLReaderFactory.createXMLReader(); XMLReader xmlReader = org.xml.sax.helpers.XMLReaderFactory.createXMLReader();
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
@ -207,11 +209,12 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
return new SAXSource(xmlReader, new InputSource(new ByteArrayInputStream(bytes))); return new SAXSource(xmlReader, new InputSource(new ByteArrayInputStream(bytes)));
} }
catch (SAXException ex) { catch (SAXException ex) {
throw new HttpMessageNotReadableException("Could not parse document: " + ex.getMessage(), ex); throw new HttpMessageNotReadableException(
"Could not parse document: " + ex.getMessage(), ex, inputMessage);
} }
} }
private Source readStAXSource(InputStream body) { private Source readStAXSource(InputStream body, HttpInputMessage inputMessage) {
try { try {
XMLInputFactory inputFactory = XMLInputFactory.newInstance(); XMLInputFactory inputFactory = XMLInputFactory.newInstance();
inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, isSupportDtd()); inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, isSupportDtd());
@ -223,7 +226,8 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
return new StAXSource(streamReader); return new StAXSource(streamReader);
} }
catch (XMLStreamException ex) { catch (XMLStreamException ex) {
throw new HttpMessageNotReadableException("Could not parse document: " + ex.getMessage(), ex); throw new HttpMessageNotReadableException(
"Could not parse document: " + ex.getMessage(), ex, inputMessage);
} }
} }

12
spring-web/src/test/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverterTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -87,7 +87,7 @@ public class MarshallingHttpMessageConverterTests {
assertEquals("Invalid result", body, result); assertEquals("Invalid result", body, result);
} }
@Test(expected = TypeMismatchException.class) @Test
public void readWithTypeMismatchException() throws Exception { public void readWithTypeMismatchException() throws Exception {
MockHttpInputMessage inputMessage = new MockHttpInputMessage(new byte[0]); MockHttpInputMessage inputMessage = new MockHttpInputMessage(new byte[0]);
@ -96,7 +96,13 @@ public class MarshallingHttpMessageConverterTests {
given(unmarshaller.unmarshal(isA(StreamSource.class))).willReturn(Integer.valueOf(3)); given(unmarshaller.unmarshal(isA(StreamSource.class))).willReturn(Integer.valueOf(3));
MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(marshaller, unmarshaller); MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(marshaller, unmarshaller);
converter.read(String.class, inputMessage); try {
converter.read(String.class, inputMessage);
fail("Should have thrown HttpMessageNotReadableException");
}
catch (HttpMessageNotReadableException ex) {
assertTrue(ex.getCause() instanceof TypeMismatchException);
}
} }
@Test @Test

2
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java

@ -212,7 +212,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
} }
} }
catch (IOException ex) { catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex); throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
} }
if (body == NO_VALUE) { if (body == NO_VALUE) {

2
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java

@ -157,7 +157,7 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
Object arg = readWithMessageConverters(inputMessage, parameter, paramType); Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null && checkRequired(parameter)) { if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " + throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString()); parameter.getExecutable().toGenericString(), inputMessage);
} }
return arg; return arg;
} }

Loading…
Cancel
Save