acceptå
RequestedContentTypeResolver
å¤çmediaTypeçæ¥å£ã
public interface RequestedContentTypeResolver {
List<MediaType> MEDIA_TYPE_ALL_LIST = Collections.singletonList(MediaType.ALL);
/**å°ç»å®ç请æ±è§£æ为请æ±çåªä½ç±»åå表ãè¿åçå表é¦å
æç¹å¼æ§æåºï¼ç¶åæè´¨éåæ°æåºã*/
List<MediaType> resolveMediaTypes(ServerWebExchange exchange);
}
FixedContentTypeResolver
解æå¨å§ç»è§£æ为åºå®å表çMediaTypeãè¿å¯ä»¥ç¨ä½âæåä¸è¡âçç¥ï¼å½å®¢æ·ç«¯æ²¡æ请æ±ä»»ä½åªä½ç±»åæ¶æä¾è§£æã
public class FixedContentTypeResolver implements RequestedContentTypeResolver {
private final List<MediaType> contentTypes;
}
HeaderContentTypeResolver
æ ¹æ®è¯·æ±çâAcceptâ header 解æã
public class HeaderContentTypeResolver implements RequestedContentTypeResolver {
@Override
public List<MediaType> resolveMediaTypes(ServerWebExchange exchange) throws NotAcceptableStatusException {
try {
//Accept headerã
List<MediaType> mediaTypes = exchange.getRequest().getHeaders().getAccept();
MediaType.sortBySpecificityAndQuality(mediaTypes);
return (!CollectionUtils.isEmpty(mediaTypes) ? mediaTypes : MEDIA_TYPE_ALL_LIST);
}
catch (InvalidMediaTypeException ex) {
String value = exchange.getRequest().getHeaders().getFirst("Accept");
throw new NotAcceptableStatusException(
"Could not parse 'Accept' header [" + value + "]: " + ex.getMessage());
}
}
}
ParameterContentTypeResolver
æ ¹æ®æ¥è¯¢åæ°è§£æMediaTypeãåæ°èªå®ä¹ã
public class ParameterContentTypeResolver implements RequestedContentTypeResolver {
/** Primary lookup for media types by key (e.g. "json" -> "application/json") */
private final Map<String, MediaType> mediaTypes = new ConcurrentHashMap<>(64);
private String parameterName = "format";
@Override
public List<MediaType> resolveMediaTypes(ServerWebExchange exchange) throws NotAcceptableStatusException {
String key = exchange.getRequest().getQueryParams().getFirst(getParameterName());
if (!StringUtils.hasText(key)) {
return MEDIA_TYPE_ALL_LIST;
}
//éè¿map æ¥æ¾å¯¹åºMediaType
key = formatKey(key);
MediaType match = this.mediaTypes.get(key);
if (match == null) {
match = MediaTypeFactory.getMediaType("filename." + key)
.orElseThrow(() -> {
List<MediaType> supported = new ArrayList<>(this.mediaTypes.values());
return new NotAcceptableStatusException(supported);
});
}
this.mediaTypes.putIfAbsent(key, match);
return Collections.singletonList(match);
}
}
resultå
HandlerResultHandlerSupport
HandlerResultHandlerçåºç±»ï¼æ¯æå 容ååå访é®ReactiveAdapter注å表ã
public abstract class HandlerResultHandlerSupport implements Ordered {
private static final List<MediaType> ALL_APPLICATION_MEDIA_TYPES =
Arrays.asList(MediaType.ALL, new MediaType("application"));
private final RequestedContentTypeResolver contentTypeResolver;
private final ReactiveAdapterRegistry adapterRegistry;
private int order = LOWEST_PRECEDENCE;
/** è·åæåéçmediaType*/
@Nullable
protected MediaType selectMediaType(
ServerWebExchange exchange, Supplier<List<MediaType>> producibleTypesSupplier) {
//å¦æheaderä¸æcontentTypeï¼ç´æ¥è¿åã
MediaType contentType = exchange.getResponse().getHeaders().getContentType();
if (contentType != null && contentType.isConcrete()) {
if (logger.isDebugEnabled()) {
logger.debug(exchange.getLogPrefix() + "Found 'Content-Type:" + contentType + "' in response");
}
return contentType;
}
//æ¥æ¾åéçMediatype
List<MediaType> acceptableTypes = getAcceptableTypes(exchange);
List<MediaType> producibleTypes = getProducibleTypes(exchange, producibleTypesSupplier);
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<>();
for (MediaType acceptable : acceptableTypes) {
for (MediaType producible : producibleTypes) {
if (acceptable.isCompatibleWith(producible)) {
compatibleMediaTypes.add(selectMoreSpecificMediaType(acceptable, producible));
}
}
}
List<MediaType> result = new ArrayList<>(compatibleMediaTypes);
MediaType.sortBySpecificityAndQuality(result);
MediaType selected = null;
for (MediaType mediaType : result) {
if (mediaType.isConcrete()) {
selected = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selected = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (selected != null) {
selected = selected.removeQualityValue();
... LOG ....
}
else if (logger.isDebugEnabled()) {
... LOG ...
}
return selected;
}
}
Viewå
View
Viewæ¥å£ç¨äºæ¸²æHandlerResultãè§å¾é常æ¯éè¿å称æ¥éæ©çï¼å¹¶ä½¿ç¨ViewResolveræ¥è§£æï¼ä¾å¦å°å ¶ä¸HTML模æ¿å¹é ãæ¤å¤ï¼è§å¾å¯ä»¥åºäºæ¨¡åä¸å å«çå¤ä¸ªå±æ§åç°ãè§å¾è¿å¯ä»¥éæ©ä»æ¨¡åä¸éæ©ä¸ä¸ªå±æ§ï¼ä½¿ç¨ä»»ä½ç°æçç¼ç å¨æ¥åç°æ¿ä»£åªä½ç±»åã
public interface View {
String BINDING_CONTEXT_ATTRIBUTE = View.class.getName() + ".bindingContext";
/**
* Return the list of media types this View supports, or an empty list.
*/
default List<MediaType> getSupportedMediaTypes() {
return Collections.emptyList();
}
/**
* Whether this View does rendering by performing a redirect.
*/
default boolean isRedirectView() {
return false;
}
/** 渲æ HandlerResult*/
Mono<Void> render(@Nullable Map<String, ?> model, @Nullable MediaType contentType, ServerWebExchange exchange);
}
Diagram
AbstractView
ublic abstract class AbstractView implements View, BeanNameAware, ApplicationContextAware {
/** Well-known name for the RequestDataValueProcessor in the bean factory. */
public static final String REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME = "requestDataValueProcessor";
protected final Log logger = LogFactory.getLog(getClass());
private final ReactiveAdapterRegistry adapterRegistry;
private final List<MediaType> mediaTypes = new ArrayList<>(4);
private Charset defaultCharset = StandardCharsets.UTF_8;
@Nullable
private String requestContextAttribute;
@Nullable
private String beanName;
@Nullable
private ApplicationContext applicationContext;
/**渲æModel*/
@Override
public Mono<Void> render(@Nullable Map<String, ?> model, @Nullable MediaType contentType,
ServerWebExchange exchange) {
... ...
//设置contentType
if (contentType != null) {
exchange.getResponse().getHeaders().setContentType(contentType);
}
return getModelAttributes(model, exchange).flatMap(mergedModel -> {
// Expose RequestContext?
if (this.requestContextAttribute != null) {
mergedModel.put(this.requestContextAttribute, createRequestContext(exchange, mergedModel));
}
//渲æmodel
return renderInternal(mergedModel, contentType, exchange);
});
}
}
AbstractUrlBasedView
åºäºUrlçè§å¾æ¸²æ
public abstract class AbstractUrlBasedView extends AbstractView implements InitializingBean {
@Nullable
private String url;
}
RedirectView
éå®åå°ä¸ä¸ªUrlã
ScriptTemplateView
解éæ§æ¨¡æ¿è§å¾ã
FreeMarkerView
FreeMarkerè§å¾
ViewResolver
ViewResolver
éè¿name解ææViewã
public interface ViewResolver {
Mono<View> resolveViewName(String viewName, Locale locale);
}
Diagram
Rendering
/**Public API for HTML rendering
*/
public interface Rendering {
Object view();
/**
* Return attributes to add to the model.
*/
Map<String, Object> modelAttributes();
/**
* Return the HTTP status to set the response to.
*/
@Nullable
HttpStatus status();
/**
* Return headers to add to the response.
*/
HttpHeaders headers();
}
conditionå
RequestCondition
RequestCondition
对ä¸ä¸ªè¯·æ±å¹é æ¡ä»¶çæ¦å¿µå»ºæ¨¡ãæç»çå®ç°ç±»å¯è½æ¯é对以ä¸æ åµä¹ä¸ï¼è·¯å¾å¹é ï¼å¤´é¨å¹é ï¼è¯·æ±åæ°å¹é ï¼å¯äº§ç
MIME
å¹é ï¼å¯æ¶è´¹
MIME
å¹é ï¼è¯·æ±æ¹æ³å¹é ï¼æè æ¯ä»¥ä¸åç§æ åµçå¹é æ¡ä»¶çä¸ä¸ªç»åã
public interface RequestCondition<T> {
T combine(T other);
@Nullable
T getMatchingCondition(ServerWebExchange exchange);
int compareTo(T other, ServerWebExchange exchange);
}
methodå
类似Mvcï¼ç¨äºå¤ç Methodç±»åçhandlerã
functionå
ä¸äºå¸¸ç¨çå½æ°å¼æ¥å£åå®ç°ã
socketå
ååºå¼WebSocket交äºçæ½è±¡åæ¯æç±»ã
WebSocketHandler
public interface WebSocketHandler {
default List<String> getSubProtocols() {
return Collections.emptyList();
}
Mono<Void> handle(WebSocketSession session);
}