001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.http.codec; 018 019import java.util.List; 020import java.util.Map; 021 022import org.apache.commons.logging.Log; 023import reactor.core.publisher.Flux; 024import reactor.core.publisher.Mono; 025 026import org.springframework.core.ResolvableType; 027import org.springframework.core.codec.AbstractDecoder; 028import org.springframework.core.codec.Decoder; 029import org.springframework.core.codec.Hints; 030import org.springframework.http.HttpLogging; 031import org.springframework.http.HttpMessage; 032import org.springframework.http.MediaType; 033import org.springframework.http.ReactiveHttpInputMessage; 034import org.springframework.http.server.reactive.ServerHttpRequest; 035import org.springframework.http.server.reactive.ServerHttpResponse; 036import org.springframework.lang.Nullable; 037import org.springframework.util.Assert; 038 039/** 040 * {@code HttpMessageReader} that wraps and delegates to a {@link Decoder}. 041 * 042 * <p>Also a {@code HttpMessageReader} that pre-resolves decoding hints 043 * from the extra information available on the server side such as the request 044 * or controller method parameter annotations. 045 * 046 * @author Arjen Poutsma 047 * @author Sebastien Deleuze 048 * @author Rossen Stoyanchev 049 * @since 5.0 050 * @param <T> the type of objects in the decoded output stream 051 */ 052public class DecoderHttpMessageReader<T> implements HttpMessageReader<T> { 053 054 private final Decoder<T> decoder; 055 056 private final List<MediaType> mediaTypes; 057 058 059 /** 060 * Create an instance wrapping the given {@link Decoder}. 061 */ 062 public DecoderHttpMessageReader(Decoder<T> decoder) { 063 Assert.notNull(decoder, "Decoder is required"); 064 initLogger(decoder); 065 this.decoder = decoder; 066 this.mediaTypes = MediaType.asMediaTypes(decoder.getDecodableMimeTypes()); 067 } 068 069 private static void initLogger(Decoder<?> decoder) { 070 if (decoder instanceof AbstractDecoder && 071 decoder.getClass().getName().startsWith("org.springframework.core.codec")) { 072 Log logger = HttpLogging.forLog(((AbstractDecoder<?>) decoder).getLogger()); 073 ((AbstractDecoder<?>) decoder).setLogger(logger); 074 } 075 } 076 077 078 /** 079 * Return the {@link Decoder} of this reader. 080 */ 081 public Decoder<T> getDecoder() { 082 return this.decoder; 083 } 084 085 @Override 086 public List<MediaType> getReadableMediaTypes() { 087 return this.mediaTypes; 088 } 089 090 091 @Override 092 public boolean canRead(ResolvableType elementType, @Nullable MediaType mediaType) { 093 return this.decoder.canDecode(elementType, mediaType); 094 } 095 096 @Override 097 public Flux<T> read(ResolvableType elementType, ReactiveHttpInputMessage message, Map<String, Object> hints) { 098 MediaType contentType = getContentType(message); 099 return this.decoder.decode(message.getBody(), elementType, contentType, hints); 100 } 101 102 @Override 103 public Mono<T> readMono(ResolvableType elementType, ReactiveHttpInputMessage message, Map<String, Object> hints) { 104 MediaType contentType = getContentType(message); 105 return this.decoder.decodeToMono(message.getBody(), elementType, contentType, hints); 106 } 107 108 /** 109 * Determine the Content-Type of the HTTP message based on the 110 * "Content-Type" header or otherwise default to 111 * {@link MediaType#APPLICATION_OCTET_STREAM}. 112 * @param inputMessage the HTTP message 113 * @return the MediaType, possibly {@code null}. 114 */ 115 @Nullable 116 protected MediaType getContentType(HttpMessage inputMessage) { 117 MediaType contentType = inputMessage.getHeaders().getContentType(); 118 return (contentType != null ? contentType : MediaType.APPLICATION_OCTET_STREAM); 119 } 120 121 122 // Server-side only... 123 124 @Override 125 public Flux<T> read(ResolvableType actualType, ResolvableType elementType, 126 ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> hints) { 127 128 Map<String, Object> allHints = Hints.merge(hints, 129 getReadHints(actualType, elementType, request, response)); 130 131 return read(elementType, request, allHints); 132 } 133 134 @Override 135 public Mono<T> readMono(ResolvableType actualType, ResolvableType elementType, 136 ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> hints) { 137 138 Map<String, Object> allHints = Hints.merge(hints, 139 getReadHints(actualType, elementType, request, response)); 140 141 return readMono(elementType, request, allHints); 142 } 143 144 /** 145 * Get additional hints for decoding for example based on the server request 146 * or annotations from controller method parameters. By default, delegate to 147 * the decoder if it is an instance of {@link HttpMessageDecoder}. 148 */ 149 protected Map<String, Object> getReadHints(ResolvableType actualType, 150 ResolvableType elementType, ServerHttpRequest request, ServerHttpResponse response) { 151 152 if (this.decoder instanceof HttpMessageDecoder) { 153 HttpMessageDecoder<?> decoder = (HttpMessageDecoder<?>) this.decoder; 154 return decoder.getDecodeHints(actualType, elementType, request, response); 155 } 156 return Hints.none(); 157 } 158 159}