2525import java .util .List ;
2626import java .util .Map ;
2727import java .util .Set ;
28+ import javax .servlet .ServletException ;
2829import javax .servlet .http .HttpServletRequest ;
2930
3031import org .springframework .core .annotation .AnnotationUtils ;
3334import org .springframework .util .AntPathMatcher ;
3435import org .springframework .util .Assert ;
3536import org .springframework .util .PathMatcher ;
37+ import org .springframework .util .StringUtils ;
38+ import org .springframework .web .HttpMediaTypeNotAcceptableException ;
39+ import org .springframework .web .HttpMediaTypeNotSupportedException ;
3640import org .springframework .web .HttpRequestMethodNotSupportedException ;
3741import org .springframework .web .bind .annotation .RequestMapping ;
3842import org .springframework .web .bind .annotation .RequestMethod ;
4650import org .springframework .web .servlet .mvc .method .condition .RequestConditionFactory ;
4751
4852/**
49- * An {@link AbstractHandlerMethodMapping} variant that uses {@link RequestMappingInfo}s for the registration and
50- * the lookup of {@link HandlerMethod}s.
51- *
53+ * An {@link AbstractHandlerMethodMapping} variant that uses {@link RequestMappingInfo}s for the registration and the
54+ * lookup of {@link HandlerMethod}s.
55+ *
5256 * @author Arjen Poutsma
5357 * @author Rossen Stoyanchev
5458 * @since 3.1.0
@@ -84,10 +88,10 @@ protected void initInterceptors() {
8488 this .mappedInterceptors = MappedInterceptors .createFromDeclaredBeans (getApplicationContext ());
8589 }
8690 }
87-
91+
8892 /**
89- * {@inheritDoc}
90- * The handler determination in this method is made based on the presence of a type-level {@link Controller} annotation.
93+ * {@inheritDoc} The handler determination in this method is made based on the presence of a type-level {@link
94+ * Controller} annotation.
9195 */
9296 @ Override
9397 protected boolean isHandler (Class <?> beanType ) {
@@ -106,9 +110,8 @@ protected void handlerMethodsInitialized(Map<RequestMappingInfo, HandlerMethod>
106110 }
107111
108112 /**
109- * Provides a {@link RequestMappingInfo} for the given method.
110- * <p>Only {@link RequestMapping @RequestMapping}-annotated methods are considered.
111- * Type-level {@link RequestMapping @RequestMapping} annotations are also detected and their
113+ * Provides a {@link RequestMappingInfo} for the given method. <p>Only {@link RequestMapping @RequestMapping}-annotated
114+ * methods are considered. Type-level {@link RequestMapping @RequestMapping} annotations are also detected and their
112115 * attributes combined with method-level {@link RequestMapping @RequestMapping} attributes.
113116 *
114117 * @param method the method to create a mapping for
@@ -137,25 +140,27 @@ protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handler
137140
138141 private static RequestMappingInfo createFromRequestMapping (RequestMapping annotation ) {
139142 return new RequestMappingInfo (Arrays .asList (annotation .value ()),
140- RequestConditionFactory .parseMethods (annotation .method ()),
141- RequestConditionFactory .parseParams (annotation .params ()),
142- RequestConditionFactory .parseHeaders (annotation .headers ()),
143- RequestConditionFactory .parseConsumes (annotation .consumes (), annotation .headers ()),
144- RequestConditionFactory .parseProduces (annotation .produces (), annotation .headers ())
145- );
143+ RequestConditionFactory .parseMethods (annotation .method ()),
144+ RequestConditionFactory .parseParams (annotation .params ()),
145+ RequestConditionFactory .parseHeaders (annotation .headers ()),
146+ RequestConditionFactory .parseConsumes (annotation .consumes (), annotation .headers ()),
147+ RequestConditionFactory .parseProduces (annotation .produces (), annotation .headers ()));
146148 }
147-
149+
148150 @ Override
149151 protected Set <String > getMappingPaths (RequestMappingInfo mapping ) {
150152 return mapping .getPatterns ();
151153 }
152154
153155 /**
154156 * Returns a new {@link RequestMappingInfo} with attributes matching to the current request or {@code null}.
157+ *
155158 * @see RequestMappingInfo#getMatchingRequestMapping(String, HttpServletRequest, PathMatcher)
156159 */
157160 @ Override
158- protected RequestMappingInfo getMatchingMapping (RequestMappingInfo mapping , String lookupPath , HttpServletRequest request ) {
161+ protected RequestMappingInfo getMatchingMapping (RequestMappingInfo mapping ,
162+ String lookupPath ,
163+ HttpServletRequest request ) {
159164 return mapping .getMatchingRequestMapping (lookupPath , request , pathMatcher );
160165 }
161166
@@ -177,26 +182,43 @@ protected void handleMatch(RequestMappingInfo mapping, String lookupPath, HttpSe
177182
178183 /**
179184 * Iterates all {@link RequestMappingInfo}s looking for mappings that match by URL but not by HTTP method.
180- * @exception HttpRequestMethodNotSupportedException if there are matches by URL but not by HTTP method
185+ *
186+ * @throws HttpRequestMethodNotSupportedException if there are matches by URL but not by HTTP method
181187 */
182188 @ Override
183- protected HandlerMethod handleNoMatch (Set <RequestMappingInfo > requestMappingInfos , String lookupPath , HttpServletRequest request )
184- throws HttpRequestMethodNotSupportedException {
189+ protected HandlerMethod handleNoMatch (Set <RequestMappingInfo > requestMappingInfos ,
190+ String lookupPath ,
191+ HttpServletRequest request ) throws ServletException {
185192 Set <String > allowedMethods = new HashSet <String >(6 );
193+ Set <MediaType > consumableMediaTypes = new HashSet <MediaType >();
194+ Set <MediaType > producibleMediaTypes = new HashSet <MediaType >();
186195 for (RequestMappingInfo info : requestMappingInfos ) {
187- for (String pattern : info .getPatterns ()) {
188- if (pathMatcher .match (pattern , lookupPath )) {
189- for (RequestMethod method : info .getMethods ().getMethods ()) {
190- allowedMethods .add (method .name ());
191- }
196+ if (!info .getMethods ().match (request )) {
197+ for (RequestMethod method : info .getMethods ().getMethods ()) {
198+ allowedMethods .add (method .name ());
192199 }
193200 }
201+ if (!info .getConsumes ().match (request )) {
202+ consumableMediaTypes .addAll (info .getConsumes ().getMediaTypes ());
203+ }
204+ if (!info .getProduces ().match (request )) {
205+ producibleMediaTypes .addAll (info .getProduces ().getMediaTypes ());
206+ }
194207 }
195208 if (!allowedMethods .isEmpty ()) {
196- throw new HttpRequestMethodNotSupportedException (request .getMethod (),
197- allowedMethods .toArray (new String [allowedMethods .size ()]));
198-
199- } else {
209+ throw new HttpRequestMethodNotSupportedException (request .getMethod (), allowedMethods );
210+ }
211+ else if (!consumableMediaTypes .isEmpty ()) {
212+ MediaType contentType = null ;
213+ if (StringUtils .hasLength (request .getContentType ())) {
214+ contentType = MediaType .parseMediaType (request .getContentType ());
215+ }
216+ throw new HttpMediaTypeNotSupportedException (contentType , new ArrayList <MediaType >(consumableMediaTypes ));
217+ }
218+ else if (!producibleMediaTypes .isEmpty ()) {
219+ throw new HttpMediaTypeNotAcceptableException (new ArrayList <MediaType >(producibleMediaTypes ));
220+ }
221+ else {
200222 return null ;
201223 }
202224 }
@@ -218,13 +240,13 @@ protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpSer
218240 }
219241
220242 /**
221- * A comparator for {@link RequestMappingInfo}s. Effective comparison can only be done in the context of a
222- * specific request. For example not all {@link RequestMappingInfo} patterns may apply to the current request.
223- * Therefore an HttpServletRequest is required as input.
243+ * A comparator for {@link RequestMappingInfo}s. Effective comparison can only be done in the context of a specific
244+ * request. For example not all {@link RequestMappingInfo} patterns may apply to the current request. Therefore an
245+ * HttpServletRequest is required as input.
224246 *
225- * <p>Furthermore, the following assumptions are made about the input RequestMappings:
226- * <ul><li>Each RequestMappingInfo has been fully matched to the request <li>The RequestMappingInfo contains
227- * matched patterns only <li>Patterns are ordered with the best matching pattern at the top </ul>
247+ * <p>Furthermore, the following assumptions are made about the input RequestMappings: <ul><li>Each RequestMappingInfo
248+ * has been fully matched to the request <li>The RequestMappingInfo contains matched patterns only <li>Patterns are
249+ * ordered with the best matching pattern at the top </ul>
228250 *
229251 * @see RequestMappingHandlerMapping#getMatchingMapping(RequestMappingInfo, String, HttpServletRequest)
230252 */
@@ -289,26 +311,6 @@ else if (iteratorOther.hasNext()) {
289311 }
290312 }
291313
292- private int compareAcceptHeaders (List <MediaType > accept , List <MediaType > otherAccept ) {
293- for (MediaType requestAccept : this .requestAcceptHeader ) {
294- int pos1 = indexOfIncluded (requestAccept , accept );
295- int pos2 = indexOfIncluded (requestAccept , otherAccept );
296- if (pos1 != pos2 ) {
297- return pos2 - pos1 ;
298- }
299- }
300- return 0 ;
301- }
302-
303- private int indexOfIncluded (MediaType requestAccept , List <MediaType > accept ) {
304- for (int i = 0 ; i < accept .size (); i ++) {
305- if (requestAccept .includes (accept .get (i ))) {
306- return i ;
307- }
308- }
309- return -1 ;
310- }
311-
312314 }
313315
314316}
0 commit comments