406 Not Acceptable 415 Unsupported Media Type Spring MVC consumes与produces

        引言:SpringWeb MVC框架在使用的过程中,有时候会出现请求结果报406 Not Acceptable 或者415 Unsupported Media Type的错误,现在学习下为什么报这个错误以及怎么解决。

     Http协议基础知识及概念:

    HTTP(HyperText Transport Protocol)是超文本传输协议的缩写,是客户端浏览器或其他程序与Web服务器之间的应用层通信协议,关于HTTP协议的详细内容请参考RFC2616。通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息,请求消息包含请求行,头信息,实体(可能为空),响应消息包含状态行,头信息,实体。HTTP的头域包括通用头,请求头,响应头和实体头四个部分。通用头域包含请求和响应消息都支持的头域,通用头域包含Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via。对通用头域的扩展要求通讯双方都支持此扩展,如果存在不支持的通用头域,一般将会作为实体头域处理。

  通用头域:

         Cache-Control头域  指定请求和响应遵循的缓存机制

         Keep-Alive  Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接

        Date 表示消息发送的时间,时间的描述格式由rfc822定义。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时,换算成本地时间,需要知道用户所在的时区

       Pragma 用来包含实现特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1协议中,它的含义和Cache-Control:no-cache相同

   请求头域:

      请求头域可能包含下列字段Accept、Accept-Charset、Accept-Encoding、Accept-Language、Authorization、From、Host、If-Modified-Since、If-Match、If-None-Match、If-Range、If-Range、If-Unmodified-Since、Max-Forwards、Proxy-Authorization、Range、Referer、User-Agent。对请求头域的扩展要求通讯双方都支持,如果存在不支持的请求头域,一般将会作为实体头域处理。

  响应头域:

    响应头域允许服务器传递不能放在状态行的附加信息,这些域主要描述服务器的信息和Request-URI进一步的信息。响应头域包含Age、Location、Proxy-Authenticate、Public、Retry-After、Server、Vary、Warning、WWW-Authenticate。

  实体头域:

    请求消息和响应消息都可以包含实体信息,实体信息一般由实体头域和实体组成。实体头域包含关于实体的原信息,实体头包括Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header。extension-header允许客户端定义新的实体头,但是这些域可能无法被接受方识别。实体可以是一个经过编码的字节流,它的编码方式由Content-Encoding或Content-Type定义,它的长度由Content-Length或Content-Range定义。

    Http协议消息头 Content-Type和Accept

    Http协议的通信标准可以在RFC2616文件中有详细的说明,说明如下:

    Accept

14.1 Accept

   The Accept request-header field can be used to specify certain media
   types which are acceptable for the response. Accept headers can be
   used to indicate that the request is specifically limited to a small
   set of desired types, as in the case of a request for an in-line
   image.

       Accept         = "Accept" ":"
                        #( media-range [ accept-params ] )

       media-range    = ( "*/*"
                        | ( type "/" "*" )
                        | ( type "/" subtype )
                        ) *( ";" parameter )
       accept-params  = ";" "q" "=" qvalue *( accept-extension )
       accept-extension = ";" token [ "=" ( token | quoted-string ) ]

   The asterisk "*" character is used to group media types into ranges,
   with "*/*" indicating all media types and "type/*" indicating all
   subtypes of that type. The media-range MAY include media type
   parameters that are applicable to that range.

   Each media-range MAY be followed by one or more accept-params,
   beginning with the "q" parameter for indicating a relative quality
   factor. The first "q" parameter (if any) separates the media-range
   parameter(s) from the accept-params. Quality factors allow the user
   or user agent to indicate the relative degree of preference for that
   media-range, using the qvalue scale from 0 to 1 (section 3.9). The
   default value is q=1.

      Note: Use of the "q" parameter name to separate media type
      parameters from Accept extension parameters is due to historical
      practice. Although this prevents any media type parameter named
      "q" from being used with a media range, such an event is believed
      to be unlikely given the lack of any "q" parameters in the IANA
      media type registry and the rare usage of any media type
      parameters in Accept. Future media types are discouraged from
      registering any parameter named "q".





Fielding, et al.            Standards Track                   [Page 100]
 
RFC 2616                        HTTP/1.1                       June 1999


   The example

       Accept: audio/*; q=0.2, audio/basic

   SHOULD be interpreted as "I prefer audio/basic, but send me any audio
   type if it is the best available after an 80% mark-down in quality."

   If no Accept header field is present, then it is assumed that the
   client accepts all media types. If an Accept header field is present,
   and if the server cannot send a response which is acceptable
   according to the combined Accept field value, then the server SHOULD
   send a 406 (not acceptable) response.

   A more elaborate example is

       Accept: text/plain; q=0.5, text/html,
               text/x-dvi; q=0.8, text/x-c

   Verbally, this would be interpreted as "text/html and text/x-c are
   the preferred media types, but if they do not exist, then send the
   text/x-dvi entity, and if that does not exist, send the text/plain
   entity."

   Media ranges can be overridden by more specific media ranges or
   specific media types. If more than one media range applies to a given
   type, the most specific reference has precedence. For example,

       Accept: text/*, text/html, text/html;level=1, */*

   have the following precedence:

       1) text/html;level=1
       2) text/html
       3) text/*
       4) */*

   The media type quality factor associated with a given type is
   determined by finding the media range with the highest precedence
   which matches that type. For example,

       Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1,
               text/html;level=2;q=0.4, */*;q=0.5

   would cause the following values to be associated:

       text/html;level=1         = 1
       text/html                 = 0.7
       text/plain                = 0.3



Fielding, et al.            Standards Track                   [Page 101]
 
RFC 2616                        HTTP/1.1                       June 1999


       image/jpeg                = 0.5
       text/html;level=2         = 0.4
       text/html;level=3         = 0.7

      Note: A user agent might be provided with a default set of quality
      values for certain media ranges. However, unless the user agent is
      a closed system which cannot interact with other rendering agents,
      this default set ought to be configurable by the user.

详细的中文大家可以网上翻译,大致的意思就是Accept请求头域可以被用来指定响应可接受的媒体类型,Accept请求头域用来表明请求被明确地限制到一个小的需要的类型集合内,正如一个请求内嵌图片的情况。在指定格式的同时还可以通过q参数指定权重。

406 Not Acceptable

The resource identified by the request is only capable of generating
   response entities which have content characteristics not acceptable
   according to the accept headers sent in the request.

   Unless it was a HEAD request, the response SHOULD include an entity
   containing a list of available entity characteristics and location(s)
   from which the user or user agent can choose the one most
   appropriate. The entity format is specified by the media type given
   in the Content-Type header field. Depending upon the format and the
   capabilities of the user agent, selection of the most appropriate
   choice MAY be performed automatically. However, this specification
   does not define any standard for such automatic selection.

      Note: HTTP/1.1 servers are allowed to return responses which are
      not acceptable according to the accept headers sent in the
      request. In some cases, this may even be preferable to sending a
      406 response. User agents are encouraged to inspect the headers of
      an incoming response to determine if it is acceptable.

   If the response could be unacceptable, a user agent SHOULD
   temporarily stop receipt of more data and query the user for a
   decision on further actions.

    大致的意思是请求指定的资源生产的带有内容的响应实体特性根据请求中发送来的Accept头域是不可接受的。如果响应不能被接受,客户端应该临时停止接受更多的数据并且为用户查询更进一步操作的决定。简而言之就是response设置的content-type不在请求头Accept指定的范围内。

415 Unsupported Media Type

The server is refusing to service the request because the entity of
   the request is in a format not supported by the requested resource
   for the requested method.

大致的意思是服务器正在拒绝为请求服务,因为请求的实体的格式不被请求的资源的方法支持,简而言之就是服务器的处理方法不支持请求实体中的content-type类型。

SpringMVC consumes与produces

    consumes

    RequestMapping注解中参数,源码注解如下:

/**
	 * The consumable media types of the mapped request, narrowing the primary mapping.
	 * <p>The format is a single media type or a sequence of media types,
	 * with a request only mapped if the {@code Content-Type} matches one of these media types.
	 * Examples:
	 * <pre class="code">
	 * consumes = "text/plain"
	 * consumes = {"text/plain", "application/*"}
	 * </pre>
	 * Expressions can be negated by using the "!" operator, as in "!text/plain", which matches
	 * all requests with a {@code Content-Type} other than "text/plain".
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings override
	 * this consumes restriction.
	 * @see org.springframework.http.MediaType
	 * @see javax.servlet.http.HttpServletRequest#getContentType()
	 */
	String[] consumes() default {};

大致的意思是映射的请求可消费的媒体类型,缩小基本的映射。格式为一个或者一系列的媒体类型,只有请求的content-type匹配上这些媒体类型中的一个这个请求才算映射上了。

Spring MVC中,在从HandlerMapping中找到一个对应的处理链的时候,会检查该配置。在RequestMappingInfoHandlerMapping中handleNoMatch方法中有如下代码:

if (helper.hasConsumesMismatch()) {
			Set<MediaType> mediaTypes = helper.getConsumableMediaTypes();
			MediaType contentType = null;
			if (StringUtils.hasLength(request.getContentType())) {
				try {
					contentType = MediaType.parseMediaType(request.getContentType());
				}
				catch (InvalidMediaTypeException ex) {
					throw new HttpMediaTypeNotSupportedException(ex.getMessage());
				}
			}
			throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(mediaTypes));
		}

在没有匹配上的时候会抛出HttpMediaTypeNotSupportedException,而这个异常会被Spring MVC在最后DefaultHandlerExceptionResolver中封装为415错误码的异常页面,源码如下:

/**
	 * Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters}
	 * were found for the PUT or POSTed content.
	 * <p>The default implementation sends an HTTP 415 error, sets the "Accept" header,
	 * and returns an empty {@code ModelAndView}. Alternatively, a fallback view could
	 * be chosen, or the HttpMediaTypeNotSupportedException could be rethrown as-is.
	 * @param ex the HttpMediaTypeNotSupportedException to be handled
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler
	 * @return an empty ModelAndView indicating the exception was handled
	 * @throws IOException potentially thrown from response.sendError()
	 */
	protected ModelAndView handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
			HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {

		response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
		List<MediaType> mediaTypes = ex.getSupportedMediaTypes();
		if (!CollectionUtils.isEmpty(mediaTypes)) {
			response.setHeader("Accept", MediaType.toString(mediaTypes));
		}
		return new ModelAndView();
	}

返回的错误页面如下: 

produces

requestMapping注解参数,注释如下:

/**
	 * The producible media types of the mapped request, narrowing the primary mapping.
	 * <p>The format is a single media type or a sequence of media types,
	 * with a request only mapped if the {@code Accept} matches one of these media types.
	 * Examples:
	 * <pre class="code">
	 * produces = "text/plain"
	 * produces = {"text/plain", "application/*"}
	 * produces = "application/json; charset=UTF-8"
	 * </pre>
	 * <p>It affects the actual content type written, for example to produce a JSON response
	 * with UTF-8 encoding, {@code "application/json; charset=UTF-8"} should be used.
	 * <p>Expressions can be negated by using the "!" operator, as in "!text/plain", which matches
	 * all requests with a {@code Accept} other than "text/plain".
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings override
	 * this produces restriction.
	 * @see org.springframework.http.MediaType
	 */
	String[] produces() default {};

匹配的请求资源可生产的媒体类型,限制基本的映射。格式是一个或者一系列的媒体类型,只有一个请求的Accept头中的媒体类型匹配到这个参数指定的媒体类型中的至少一个,才算请求匹配上了。这个参数还影响了实际的响应实体的类型。同理,在SpringMVC中也会检查该配置参数与实际请求的匹配情况,也是在RequestMappingInfoHandlerMapping中handleNoMatch方法中有如下代码:

if (helper.hasProducesMismatch()) {
			Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
			throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(mediaTypes));
		}

在DefaultHandlerExceptionResolver中也会封装该异常为406结果页面

/**
	 * Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters}
	 * were found that were acceptable for the client (expressed via the {@code Accept} header.
	 * <p>The default implementation sends an HTTP 406 error and returns an empty {@code ModelAndView}.
	 * Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotAcceptableException
	 * could be rethrown as-is.
	 * @param ex the HttpMediaTypeNotAcceptableException to be handled
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler
	 * @return an empty ModelAndView indicating the exception was handled
	 * @throws IOException potentially thrown from response.sendError()
	 */
	protected ModelAndView handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
			HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {

		response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
		return new ModelAndView();
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值