Search in sources :

Example 1 with GenericHttpMessageConverter

use of org.springframework.http.converter.GenericHttpMessageConverter in project spring-framework by spring-projects.

the class RestTemplateTests method exchangeParameterizedType.

@Test
@SuppressWarnings("rawtypes")
void exchangeParameterizedType() throws Exception {
    GenericHttpMessageConverter converter = mock(GenericHttpMessageConverter.class);
    template.setMessageConverters(Collections.<HttpMessageConverter<?>>singletonList(converter));
    ParameterizedTypeReference<List<Integer>> intList = new ParameterizedTypeReference<>() {
    };
    given(converter.canRead(intList.getType(), null, null)).willReturn(true);
    given(converter.getSupportedMediaTypes(any())).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
    given(converter.canWrite(String.class, String.class, null)).willReturn(true);
    HttpHeaders requestHeaders = new HttpHeaders();
    mockSentRequest(POST, "https://example.com", requestHeaders);
    List<Integer> expected = Collections.singletonList(42);
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setContentType(MediaType.TEXT_PLAIN);
    responseHeaders.setContentLength(10);
    mockResponseStatus(HttpStatus.OK);
    given(response.getHeaders()).willReturn(responseHeaders);
    given(response.getBody()).willReturn(new ByteArrayInputStream(Integer.toString(42).getBytes()));
    given(converter.canRead(intList.getType(), null, MediaType.TEXT_PLAIN)).willReturn(true);
    given(converter.read(eq(intList.getType()), eq(null), any(HttpInputMessage.class))).willReturn(expected);
    HttpHeaders entityHeaders = new HttpHeaders();
    entityHeaders.set("MyHeader", "MyValue");
    HttpEntity<String> requestEntity = new HttpEntity<>("Hello World", entityHeaders);
    ResponseEntity<List<Integer>> result = template.exchange("https://example.com", POST, requestEntity, intList);
    assertThat(result.getBody()).as("Invalid POST result").isEqualTo(expected);
    assertThat(result.getHeaders().getContentType()).as("Invalid Content-Type").isEqualTo(MediaType.TEXT_PLAIN);
    assertThat(requestHeaders.getFirst("Accept")).as("Invalid Accept header").isEqualTo(MediaType.TEXT_PLAIN_VALUE);
    assertThat(requestHeaders.getFirst("MyHeader")).as("Invalid custom header").isEqualTo("MyValue");
    assertThat(result.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.OK);
    verify(response).close();
}
Also used : HttpInputMessage(org.springframework.http.HttpInputMessage) HttpHeaders(org.springframework.http.HttpHeaders) HttpEntity(org.springframework.http.HttpEntity) ByteArrayInputStream(java.io.ByteArrayInputStream) ParameterizedTypeReference(org.springframework.core.ParameterizedTypeReference) List(java.util.List) GenericHttpMessageConverter(org.springframework.http.converter.GenericHttpMessageConverter) Test(org.junit.jupiter.api.Test)

Example 2 with GenericHttpMessageConverter

use of org.springframework.http.converter.GenericHttpMessageConverter in project spring-framework by spring-projects.

the class HttpMessageConverterExtractor method extractData.

@Override
@SuppressWarnings({ "unchecked", "rawtypes", "resource" })
public T extractData(ClientHttpResponse response) throws IOException {
    MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
    if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
        return null;
    }
    MediaType contentType = getContentType(responseWrapper);
    try {
        for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
            if (messageConverter instanceof GenericHttpMessageConverter) {
                GenericHttpMessageConverter<?> genericMessageConverter = (GenericHttpMessageConverter<?>) messageConverter;
                if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
                    if (logger.isDebugEnabled()) {
                        ResolvableType resolvableType = ResolvableType.forType(this.responseType);
                        logger.debug("Reading to [" + resolvableType + "]");
                    }
                    return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
                }
            }
            if (this.responseClass != null) {
                if (messageConverter.canRead(this.responseClass, contentType)) {
                    if (logger.isDebugEnabled()) {
                        String className = this.responseClass.getName();
                        logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
                    }
                    return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
                }
            }
        }
    } catch (IOException | HttpMessageNotReadableException ex) {
        throw new RestClientException("Error while extracting response for type [" + this.responseType + "] and content type [" + contentType + "]", ex);
    }
    throw new UnknownContentTypeException(this.responseType, contentType, responseWrapper.getRawStatusCode(), responseWrapper.getStatusText(), responseWrapper.getHeaders(), getResponseBody(responseWrapper));
}
Also used : IOException(java.io.IOException) HttpMessageNotReadableException(org.springframework.http.converter.HttpMessageNotReadableException) MediaType(org.springframework.http.MediaType) ResolvableType(org.springframework.core.ResolvableType) GenericHttpMessageConverter(org.springframework.http.converter.GenericHttpMessageConverter)

Example 3 with GenericHttpMessageConverter

use of org.springframework.http.converter.GenericHttpMessageConverter in project spring-framework by spring-projects.

the class AbstractMessageConverterMethodProcessor method writeWithMessageConverters.

/**
 * Writes the given return type to the given output message.
 * @param value the value to write to the output message
 * @param returnType the type of the value
 * @param inputMessage the input messages. Used to inspect the {@code Accept} header.
 * @param outputMessage the output message to write to
 * @throws IOException thrown in case of I/O errors
 * @throws HttpMediaTypeNotAcceptableException thrown when the conditions indicated
 * by the {@code Accept} header on the request cannot be met by the message converters
 * @throws HttpMessageNotWritableException thrown if a given message cannot
 * be written by a converter, or if the content-type chosen by the server
 * has no compatible converter.
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    Object body;
    Class<?> valueType;
    Type targetType;
    if (value instanceof CharSequence) {
        body = value.toString();
        valueType = String.class;
        targetType = String.class;
    } else {
        body = value;
        valueType = getReturnValueType(body, returnType);
        targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
    }
    if (isResourceType(value, returnType)) {
        outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
        if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null && outputMessage.getServletResponse().getStatus() == 200) {
            Resource resource = (Resource) value;
            try {
                List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
                outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
                body = HttpRange.toResourceRegions(httpRanges, resource);
                valueType = body.getClass();
                targetType = RESOURCE_REGION_LIST_TYPE;
            } catch (IllegalArgumentException ex) {
                outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
                outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
            }
        }
    }
    MediaType selectedMediaType = null;
    MediaType contentType = outputMessage.getHeaders().getContentType();
    boolean isContentTypePreset = contentType != null && contentType.isConcrete();
    if (isContentTypePreset) {
        if (logger.isDebugEnabled()) {
            logger.debug("Found 'Content-Type:" + contentType + "' in response");
        }
        selectedMediaType = contentType;
    } else {
        HttpServletRequest request = inputMessage.getServletRequest();
        List<MediaType> acceptableTypes;
        try {
            acceptableTypes = getAcceptableMediaTypes(request);
        } catch (HttpMediaTypeNotAcceptableException ex) {
            int series = outputMessage.getServletResponse().getStatus() / 100;
            if (body == null || series == 4 || series == 5) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignoring error response content (if any). " + ex);
                }
                return;
            }
            throw ex;
        }
        List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
        if (body != null && producibleTypes.isEmpty()) {
            throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);
        }
        List<MediaType> mediaTypesToUse = new ArrayList<>();
        for (MediaType requestedType : acceptableTypes) {
            for (MediaType producibleType : producibleTypes) {
                if (requestedType.isCompatibleWith(producibleType)) {
                    mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
                }
            }
        }
        if (mediaTypesToUse.isEmpty()) {
            if (body != null) {
                throw new HttpMediaTypeNotAcceptableException(producibleTypes);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
            }
            return;
        }
        MimeTypeUtils.sortBySpecificity(mediaTypesToUse);
        for (MediaType mediaType : mediaTypesToUse) {
            if (mediaType.isConcrete()) {
                selectedMediaType = mediaType;
                break;
            } else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
                selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                break;
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes);
        }
    }
    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) {
                body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage);
                if (body != null) {
                    Object theBody = body;
                    LogFormatUtils.traceDebug(logger, traceOn -> "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
                    addContentDispositionHeader(inputMessage, outputMessage);
                    if (genericConverter != null) {
                        genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                    } else {
                        ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                    }
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Nothing to write: null body");
                    }
                }
                return;
            }
        }
    }
    if (body != null) {
        Set<MediaType> producibleMediaTypes = (Set<MediaType>) inputMessage.getServletRequest().getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
        if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
            throw new HttpMessageNotWritableException("No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
        }
        throw new HttpMediaTypeNotAcceptableException(getSupportedMediaTypes(body.getClass()));
    }
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) HttpMediaTypeNotAcceptableException(org.springframework.web.HttpMediaTypeNotAcceptableException) InputStreamResource(org.springframework.core.io.InputStreamResource) Resource(org.springframework.core.io.Resource) ArrayList(java.util.ArrayList) HttpServletRequest(jakarta.servlet.http.HttpServletRequest) ResolvableType(org.springframework.core.ResolvableType) MediaType(org.springframework.http.MediaType) Type(java.lang.reflect.Type) HttpMessageNotWritableException(org.springframework.http.converter.HttpMessageNotWritableException) HttpMessageConverter(org.springframework.http.converter.HttpMessageConverter) GenericHttpMessageConverter(org.springframework.http.converter.GenericHttpMessageConverter) MediaType(org.springframework.http.MediaType) HttpRange(org.springframework.http.HttpRange) GenericHttpMessageConverter(org.springframework.http.converter.GenericHttpMessageConverter)

Example 4 with GenericHttpMessageConverter

use of org.springframework.http.converter.GenericHttpMessageConverter in project spring-framework by spring-projects.

the class AbstractMessageConverterMethodArgumentResolver method readWithMessageConverters.

/**
 * Create the method argument value of the expected parameter type by reading
 * from the given HttpInputMessage.
 * @param <T> the expected type of the argument value to be created
 * @param inputMessage the HTTP input message representing the current request
 * @param parameter the method parameter descriptor
 * @param targetType the target type, not necessarily the same as the method
 * parameter type, e.g. for {@code HttpEntity<String>}.
 * @return the created method argument value
 * @throws IOException if the reading from the request fails
 * @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
 */
@SuppressWarnings("unchecked")
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    MediaType contentType;
    boolean noContentType = false;
    try {
        contentType = inputMessage.getHeaders().getContentType();
    } catch (InvalidMediaTypeException ex) {
        throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    if (contentType == null) {
        noContentType = true;
        contentType = MediaType.APPLICATION_OCTET_STREAM;
    }
    Class<?> contextClass = parameter.getContainingClass();
    Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    if (targetClass == null) {
        ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
        targetClass = (Class<T>) resolvableType.resolve();
    }
    HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
    Object body = NO_VALUE;
    EmptyBodyCheckingHttpInputMessage message = null;
    try {
        message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
            GenericHttpMessageConverter<?> genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) : (targetClass != null && converter.canRead(targetClass, contentType))) {
                if (message.hasBody()) {
                    HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                    body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) : ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                    body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                } else {
                    body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                }
                break;
            }
        }
    } catch (IOException ex) {
        throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
    } finally {
        if (message != null && message.hasBody()) {
            closeStreamIfNecessary(message.getBody());
        }
    }
    if (body == NO_VALUE) {
        if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && !message.hasBody())) {
            return null;
        }
        throw new HttpMediaTypeNotSupportedException(contentType, getSupportedMediaTypes(targetClass != null ? targetClass : Object.class));
    }
    MediaType selectedContentType = contentType;
    Object theBody = body;
    LogFormatUtils.traceDebug(logger, traceOn -> {
        String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
        return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
    });
    return body;
}
Also used : HttpRequest(org.springframework.http.HttpRequest) ServletServerHttpRequest(org.springframework.http.server.ServletServerHttpRequest) HttpInputMessage(org.springframework.http.HttpInputMessage) IOException(java.io.IOException) HttpMediaTypeNotSupportedException(org.springframework.web.HttpMediaTypeNotSupportedException) HttpMessageNotReadableException(org.springframework.http.converter.HttpMessageNotReadableException) HttpMessageConverter(org.springframework.http.converter.HttpMessageConverter) GenericHttpMessageConverter(org.springframework.http.converter.GenericHttpMessageConverter) MediaType(org.springframework.http.MediaType) ResolvableType(org.springframework.core.ResolvableType) HttpMethod(org.springframework.http.HttpMethod) InvalidMediaTypeException(org.springframework.http.InvalidMediaTypeException) GenericHttpMessageConverter(org.springframework.http.converter.GenericHttpMessageConverter) Nullable(org.springframework.lang.Nullable)

Aggregations

GenericHttpMessageConverter (org.springframework.http.converter.GenericHttpMessageConverter)4 ResolvableType (org.springframework.core.ResolvableType)3 MediaType (org.springframework.http.MediaType)3 IOException (java.io.IOException)2 HttpInputMessage (org.springframework.http.HttpInputMessage)2 HttpMessageConverter (org.springframework.http.converter.HttpMessageConverter)2 HttpMessageNotReadableException (org.springframework.http.converter.HttpMessageNotReadableException)2 HttpServletRequest (jakarta.servlet.http.HttpServletRequest)1 ByteArrayInputStream (java.io.ByteArrayInputStream)1 Type (java.lang.reflect.Type)1 ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Set (java.util.Set)1 Test (org.junit.jupiter.api.Test)1 ParameterizedTypeReference (org.springframework.core.ParameterizedTypeReference)1 InputStreamResource (org.springframework.core.io.InputStreamResource)1 Resource (org.springframework.core.io.Resource)1 HttpEntity (org.springframework.http.HttpEntity)1 HttpHeaders (org.springframework.http.HttpHeaders)1