@Test public void getMatchingCondition() throws Exception { MockServerWebExchange exchange = postExchange("text/plain"); ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain", "application/xml"); ConsumesRequestCondition result = condition.getMatchingCondition(exchange); assertConditions(result, "text/plain"); condition = new ConsumesRequestCondition("application/xml"); result = condition.getMatchingCondition(exchange); assertNull(result); }
@Test public void combine() throws Exception { ConsumesRequestCondition condition1 = new ConsumesRequestCondition("text/plain"); ConsumesRequestCondition condition2 = new ConsumesRequestCondition("application/xml"); ConsumesRequestCondition result = condition1.combine(condition2); assertEquals(condition2, result); }
@Test public void compareToMultiple() throws Exception { MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/")); ConsumesRequestCondition condition1 = new ConsumesRequestCondition("*/*", "text/plain"); ConsumesRequestCondition condition2 = new ConsumesRequestCondition("text/*", "text/plain;q=0.7"); int result = condition1.compareTo(condition2, exchange); assertTrue("Invalid comparison result: " + result, result < 0); result = condition2.compareTo(condition1, exchange); assertTrue("Invalid comparison result: " + result, result > 0); }
/** * Checks if any of the contained media type expressions match the given * request 'Content-Type' header and returns an instance that is guaranteed * to contain matching expressions only. The match is performed via * {@link MediaType#includes(MediaType)}. * @param exchange the current exchange * @return the same instance if the condition contains no expressions; * or a new condition with matching expressions only; * or {@code null} if no expressions match. */ @Override public ConsumesRequestCondition getMatchingCondition(ServerWebExchange exchange) { if (CorsUtils.isPreFlightRequest(exchange.getRequest())) { return PRE_FLIGHT_MATCH; } if (isEmpty()) { return this; } Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>(this.expressions); result.removeIf(expression -> !expression.match(exchange)); return (!result.isEmpty() ? new ConsumesRequestCondition(result) : null); }
@Test public void getConsumableMediaTypesNegatedExpression() throws Exception { ConsumesRequestCondition condition = new ConsumesRequestCondition("!application/xml"); assertEquals(Collections.emptySet(), condition.getConsumableMediaTypes()); }
public RequestMappingInfo(@Nullable String name, @Nullable PatternsRequestCondition patterns, @Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params, @Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes, @Nullable ProducesRequestCondition produces, @Nullable RequestCondition<?> custom) { this.name = (StringUtils.hasText(name) ? name : null); this.patternsCondition = (patterns != null ? patterns : new PatternsRequestCondition()); this.methodsCondition = (methods != null ? methods : new RequestMethodsRequestCondition()); this.paramsCondition = (params != null ? params : new ParamsRequestCondition()); this.headersCondition = (headers != null ? headers : new HeadersRequestCondition()); this.consumesCondition = (consumes != null ? consumes : new ConsumesRequestCondition()); this.producesCondition = (produces != null ? produces : new ProducesRequestCondition()); this.customConditionHolder = new RequestConditionHolder(custom); }
/** * Return declared "consumable" types but only among those that also * match the "methods" condition. */ public Set<MediaType> getConsumableMediaTypes() { return this.partialMatches.stream().filter(PartialMatch::hasMethodsMatch). flatMap(m -> m.getInfo().getConsumesCondition().getConsumableMediaTypes().stream()). collect(Collectors.toCollection(LinkedHashSet::new)); }
/** * Create a new {@link PartialMatch} instance. * @param info the RequestMappingInfo that matches the URL path * @param exchange the current exchange */ public PartialMatch(RequestMappingInfo info, ServerWebExchange exchange) { this.info = info; this.methodsMatch = info.getMethodsCondition().getMatchingCondition(exchange) != null; this.consumesMatch = info.getConsumesCondition().getMatchingCondition(exchange) != null; this.producesMatch = info.getProducesCondition().getMatchingCondition(exchange) != null; this.paramsMatch = info.getParamsCondition().getMatchingCondition(exchange) != null; }
@Override public String toString() { StringBuilder builder = new StringBuilder("{"); if (!this.methodsCondition.isEmpty()) { Set<RequestMethod> httpMethods = this.methodsCondition.getMethods(); builder.append(httpMethods.size() == 1 ? httpMethods.iterator().next() : httpMethods); } if (!this.patternsCondition.isEmpty()) { Set<PathPattern> patterns = this.patternsCondition.getPatterns(); builder.append(" ").append(patterns.size() == 1 ? patterns.iterator().next() : patterns); } if (!this.paramsCondition.isEmpty()) { builder.append(", params ").append(this.paramsCondition); } if (!this.headersCondition.isEmpty()) { builder.append(", headers ").append(this.headersCondition); } if (!this.consumesCondition.isEmpty()) { builder.append(", consumes ").append(this.consumesCondition); } if (!this.producesCondition.isEmpty()) { builder.append(", produces ").append(this.producesCondition); } if (!this.customConditionHolder.isEmpty()) { builder.append(", and ").append(this.customConditionHolder); } builder.append('}'); return builder.toString(); }
/** * Combines "this" request mapping info (i.e. the current instance) with another request mapping info instance. * <p>Example: combine type- and method-level request mappings. * @return a new request mapping info instance; never {@code null} */ @Override public RequestMappingInfo combine(RequestMappingInfo other) { String name = combineNames(other); PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition); RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition); ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition); HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition); ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition); ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition); RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder); return new RequestMappingInfo(name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); }
return result; result = this.consumesCondition.compareTo(other.getConsumesCondition(), exchange); if (result != 0) { return result;
@Override public int hashCode() { return (this.patternsCondition.hashCode() * 31 + // primary differentiation this.methodsCondition.hashCode() + this.paramsCondition.hashCode() + this.headersCondition.hashCode() + this.consumesCondition.hashCode() + this.producesCondition.hashCode() + this.customConditionHolder.hashCode()); }
/** * Creates a new instance with "consumes" and "header" expressions. * "Header" expressions where the header name is not 'Content-Type' or have * no header value defined are ignored. If 0 expressions are provided in * total, the condition will match to every request * @param consumes as described in {@link RequestMapping#consumes()} * @param headers as described in {@link RequestMapping#headers()} */ public ConsumesRequestCondition(String[] consumes, String[] headers) { this(parseExpressions(consumes, headers)); }
@Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof RequestMappingInfo)) { return false; } RequestMappingInfo otherInfo = (RequestMappingInfo) other; return (this.patternsCondition.equals(otherInfo.patternsCondition) && this.methodsCondition.equals(otherInfo.methodsCondition) && this.paramsCondition.equals(otherInfo.paramsCondition) && this.headersCondition.equals(otherInfo.headersCondition) && this.consumesCondition.equals(otherInfo.consumesCondition) && this.producesCondition.equals(otherInfo.producesCondition) && this.customConditionHolder.equals(otherInfo.customConditionHolder)); }
/** * Checks if any of the contained media type expressions match the given * request 'Content-Type' header and returns an instance that is guaranteed * to contain matching expressions only. The match is performed via * {@link MediaType#includes(MediaType)}. * @param exchange the current exchange * @return the same instance if the condition contains no expressions; * or a new condition with matching expressions only; * or {@code null} if no expressions match. */ @Override public ConsumesRequestCondition getMatchingCondition(ServerWebExchange exchange) { if (CorsUtils.isPreFlightRequest(exchange.getRequest())) { return PRE_FLIGHT_MATCH; } if (isEmpty()) { return this; } Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>(expressions); for (Iterator<ConsumeMediaTypeExpression> iterator = result.iterator(); iterator.hasNext();) { ConsumeMediaTypeExpression expression = iterator.next(); if (!expression.match(exchange)) { iterator.remove(); } } return (result.isEmpty()) ? null : new ConsumesRequestCondition(result); }
@Test public void parseConsumesAndHeaders() throws Exception { String[] consumes = new String[] {"text/plain"}; String[] headers = new String[]{"foo=bar", "content-type=application/xml,application/pdf"}; ConsumesRequestCondition condition = new ConsumesRequestCondition(consumes, headers); assertConditions(condition, "text/plain", "application/xml", "application/pdf"); }
@Test // SPR-14988 public void getMappingOverridesConsumesFromTypeLevelAnnotation() throws Exception { RequestMappingInfo requestMappingInfo = assertComposedAnnotationMapping(RequestMethod.GET); assertArrayEquals(new MediaType[]{MediaType.ALL}, new ArrayList<>( requestMappingInfo.getConsumesCondition().getConsumableMediaTypes()).toArray()); }
/** * Checks if all conditions in this request mapping info match the provided request and returns * a potentially new request mapping info with conditions tailored to the current request. * <p>For example the returned instance may contain the subset of URL patterns that match to * the current request, sorted with best matching patterns on top. * @return a new instance in case all conditions match; or {@code null} otherwise */ @Override @Nullable public RequestMappingInfo getMatchingCondition(ServerWebExchange exchange) { RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(exchange); ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(exchange); HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(exchange); ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(exchange); ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(exchange); if (methods == null || params == null || headers == null || consumes == null || produces == null) { return null; } PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(exchange); if (patterns == null) { return null; } RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(exchange); if (custom == null) { return null; } return new RequestMappingInfo(this.name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); }
@Test public void createEmpty() { RequestMappingInfo info = paths().build(); assertEquals(0, info.getPatternsCondition().getPatterns().size()); assertEquals(0, info.getMethodsCondition().getMethods().size()); assertEquals(true, info.getConsumesCondition().isEmpty()); assertEquals(true, info.getProducesCondition().isEmpty()); assertNotNull(info.getParamsCondition()); assertNotNull(info.getHeadersCondition()); assertNull(info.getCustomCondition()); }
/** * Combines "this" request mapping info (i.e. the current instance) with another request mapping info instance. * <p>Example: combine type- and method-level request mappings. * @return a new request mapping info instance; never {@code null} */ @Override public RequestMappingInfo combine(RequestMappingInfo other) { String name = combineNames(other); PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition); RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition); ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition); HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition); ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition); ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition); RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder); return new RequestMappingInfo(name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); }