jvm oom,Spring MVC處理響應的 header

 2023-11-05 阅读 28 评论 0

摘要:我們經常需要在HttpResponse中設置一些headers,我們使用Spring MVC框架的時候我們如何給Response設置Header呢? So easy, 看下面的代碼: @RequestMapping(value = "/rulelist", method = RequestMethod.GET)@ResponseBod

我們經常需要在HttpResponse中設置一些headers,我們使用Spring MVC框架的時候我們如何給Response設置Header呢?

So easy, 看下面的代碼:

@RequestMapping(value = "/rulelist", method = RequestMethod.GET)@ResponseBody
public String getRuleList(HttpServletRequest request,HttpServletResponse response) {response.addHeader("test", "test");return service.getRuleList();
}

jvm oom、?

通過驗證,我們可以看到test項已經被成功添加到response的頭部信息

Content-Length: 2 kilobytes
Content-Type:   text/plain;charset=ISO-8859-1
Server: Apache-Coyote/1.1
test: test

?

Spring MVC、接下來,我們希望修改Content-Type,從而統一服務器端和客戶端的內容編碼。我們繼續修改代碼,

@RequestMapping(value = "/rulelist", method = RequestMethod.GET)
@ResponseBody
public String getRuleList(HttpServletRequest request,HttpServletResponse response) {response.addHeader("Content-Type", "application/json;charset=UTF-8");return service.getRuleList();
}

?

接下來,我們驗證一下結果:

Content-Length: 2 kilobytes
Content-Type:   text/plain;charset=ISO-8859-1
Server: Apache-Coyote/1.1

?

和我們預想的并一樣,response的content-type header沒有被設置成"application/json;charset=UTF-8",很令人困惑。

那么,接下來讓我們來探索下Spring MVC內部是如何處理這一過程的。首先我們先要對Spring MVC框架處理Http請求的流程有一個整體的了解。

下圖清晰地向大家展示了Spring MVC處理HTTP請求的流程,(圖片來自網絡)

?具體流程如下:

1. DispatcherServlet接收到Request請求

2. HandlerMapping選擇一個合適的Handler處理Request請求

3-4. 選擇合適的HandlerAdapter,調用用戶編寫的Controller處理業務邏輯。(HandlerAdapter主要是幫助Spring MVC支持多種類型的Controller)

5. Controller將返回結果放置到Model中并且返回view名稱給Handler Adapter

6. DispatcherServlet選擇合適的ViewResolver來生成View對象

7-8. View對象利用Model中的數據進行渲染并返回數據

相信大家對于上面的處理流程并不陌生,上面的流程圖向我們展示了SpringMVC生成ModelAndView并返回response的大體流程。

下面我們來看看我們上面代碼片段的處理流程是如何進行的?

從上面的流程圖我們可以看到,content-type header是單獨被處理的,具體過程可以參考下面的源碼(AbstractMessageConverterMethodProcessor):

protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException {Class<?> returnValueClass = getReturnValueType(returnValue, returnType);HttpServletRequest servletRequest = inputMessage.getServletRequest();List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest); //適合的兼容media types類型實際上,我們可以使用produces = {}來指定我們需要的mediatypeList<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass);Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();for (MediaType requestedType : requestedMediaTypes) {for (MediaType producibleType : producibleMediaTypes) {if (requestedType.isCompatibleWith(producibleType)) {compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));}}}if (compatibleMediaTypes.isEmpty()) {if (returnValue != null) {throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);}return;}List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);MediaType.sortBySpecificityAndQuality(mediaTypes);MediaType selectedMediaType = null;   //選擇最匹配的mediaTypefor (MediaType mediaType : mediaTypes) {if (mediaType.isConcrete()) {selectedMediaType = mediaType;break;}else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;break;}}if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> messageConverter : this.messageConverters) {         //遍歷messageConvertors, 尋找可以處理相應返回類型和mediatype的HttpMessageConvertorif (messageConverter.canWrite(returnValueClass, selectedMediaType)) {returnValue = this.adviceChain.invoke(returnValue, returnType, selectedMediaType,(Class<HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);if (returnValue != null) {         //這里將會填充mediatype到header,并將httpmessage發送給請求者((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);if (logger.isDebugEnabled()) {logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +messageConverter + "]");}}return;}}}if (returnValue != null) {throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);}
}

?

接下來,將選擇好的mediatype寫入到HttpOutputMessage中

public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {final HttpHeaders headers = outputMessage.getHeaders();     //設置contenttype到HttpOutputMessageif (headers.getContentType() == null) {MediaType contentTypeToUse = contentType;if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {contentTypeToUse = getDefaultContentType(t);}if (contentTypeToUse != null) {headers.setContentType(contentTypeToUse);}}if (headers.getContentLength() == -1) {Long contentLength = getContentLength(t, headers.getContentType());if (contentLength != null) {headers.setContentLength(contentLength);}}/* 省略了不相干代碼 */
}

?

最終的Headers設置在ServletServerHttpResponse類中完成,

private void writeHeaders() {if (!this.headersWritten) {for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {String headerName = entry.getKey();for (String headerValue : entry.getValue()) {         //將復合類中之前設置的header(content-type)內容補充到servletResponsethis.servletResponse.addHeader(headerName, headerValue);}}// HttpServletResponse exposes some headers as properties: we should include those if not already presentif (this.servletResponse.getContentType() == null && this.headers.getContentType() != null) {this.servletResponse.setContentType(this.headers.getContentType().toString());}if (this.servletResponse.getCharacterEncoding() == null && this.headers.getContentType() != null &&this.headers.getContentType().getCharSet() != null) {this.servletResponse.setCharacterEncoding(this.headers.getContentType().getCharSet().name());}this.headersWritten = true;}
}

?

從上述的代碼中,我們可以看到在RequestResponseBodyMethodProcessor這個ReturnValueHandler中,media-type被單獨的邏輯進行處理,因此直接在ServletResponse中設置content-type header并不能正常生效。

需要在@RequestMapping中添加produces = {} 進行設置才可以。

?

參考:https://www.cnblogs.com/kaiblog/p/7565231.html

我們經常需要在HttpResponse中設置一些headers,我們使用Spring MVC框架的時候我們如何給Response設置Header呢?

Sooooooooooooo easy, 看下面的代碼:

1
2
3
4
5
6
7
@RequestMapping(value =?"/rulelist", method = RequestMethod.GET)
@ResponseBody
public?String getRuleList(HttpServletRequest request,
????????HttpServletResponse response) {
????response.addHeader("test",?"test");
????return?service.getRuleList();
}

通過驗證,我們可以看到test項已經被成功添加到response的頭部信息

1
2
3
4
Content-Length: 2 kilobytes
Content-Type:?? text/plain;charset=ISO-8859-1
Server: Apache-Coyote/1.1
test: test

接下來,我們希望修改Content-Type,從而統一服務器端和客戶端的內容編碼。我們繼續修改代碼,

1
2
3
4
5
6
7
@RequestMapping(value =?"/rulelist", method = RequestMethod.GET)
@ResponseBody
public?String getRuleList(HttpServletRequest request,
????????HttpServletResponse response) {
????response.addHeader("Content-Type",?"application/json;charset=UTF-8");
????return?service.getRuleList();
}

接下來,我們驗證一下結果:

1
2
3
Content-Length: 2 kilobytes
Content-Type:?? text/plain;charset=ISO-8859-1
Server: Apache-Coyote/1.1

和我們預想的并一樣,response的content-type header沒有被設置成"application/json;charset=UTF-8",很令人困惑。

那么,接下來讓我們來探索下Spring MVC內部是如何處理這一過程的。首先我們先要對Spring MVC框架處理Http請求的流程有一個整體的了解。

下圖清晰地向大家展示了Spring MVC處理HTTP請求的流程,(圖片來自網絡)

?

  

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

具體流程如下:

1. DispatcherServlet接收到Request請求

2. HandlerMapping選擇一個合適的Handler處理Request請求

3-4. 選擇合適的HandlerAdapter,調用用戶編寫的Controller處理業務邏輯。(HandlerAdapter主要是幫助Spring MVC支持多種類型的Controller)

5. Controller將返回結果放置到Model中并且返回view名稱給Handler Adapter

6. DispatcherServlet選擇合適的ViewResolver來生成View對象

7-8. View對象利用Model中的數據進行渲染并返回數據

相信大家對于上面的處理流程并不陌生,上面的流程圖向我們展示了SpringMVC生成ModelAndView并返回response的大體流程。

下面我們來看看我們上面代碼片段的處理流程是如何進行的?

從上面的流程圖我們可以看到,content-type header是單獨被處理的,具體過程可以參考下面的源碼(AbstractMessageConverterMethodProcessor):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
protected?<T>?void?writeWithMessageConverters(T returnValue, MethodParameter returnType,
????????ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
????????throws?IOException, HttpMediaTypeNotAcceptableException {
????Class<?> returnValueClass = getReturnValueType(returnValue, returnType);
????HttpServletRequest servletRequest = inputMessage.getServletRequest();
????List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest);?//適合的兼容media types類型實際上,我們可以使用produces = {}來指定我們需要的mediatype
????List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass);
????Set<MediaType> compatibleMediaTypes =?new?LinkedHashSet<MediaType>();
????for?(MediaType requestedType : requestedMediaTypes) {
????????for?(MediaType producibleType : producibleMediaTypes) {
????????????if?(requestedType.isCompatibleWith(producibleType)) {
????????????????compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
????????????}
????????}
????}
????if?(compatibleMediaTypes.isEmpty()) {
????????if?(returnValue !=?null) {
????????????throw?new?HttpMediaTypeNotAcceptableException(producibleMediaTypes);
????????}
????????return;
????}
????List<MediaType> mediaTypes =?new?ArrayList<MediaType>(compatibleMediaTypes);
????MediaType.sortBySpecificityAndQuality(mediaTypes);
????MediaType selectedMediaType =?null;   //選擇最匹配的mediaType
????for?(MediaType mediaType : mediaTypes) {
????????if?(mediaType.isConcrete()) {
????????????selectedMediaType = mediaType;
????????????break;
????????}
????????else?if?(mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
????????????selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
????????????break;
????????}
????}
????if?(selectedMediaType !=?null) {
????????selectedMediaType = selectedMediaType.removeQualityValue();
????????for?(HttpMessageConverter<?> messageConverter :?this.messageConverters) {?????????//遍歷messageConvertors, 尋找可以處理相應返回類型和mediatype的HttpMessageConvertor
????????????if?(messageConverter.canWrite(returnValueClass, selectedMediaType)) {
????????????????returnValue =?this.adviceChain.invoke(returnValue, returnType, selectedMediaType,
????????????????????????(Class<HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
????????????????if?(returnValue !=?null) {?????????//這里將會填充mediatype到header,并將httpmessage發送給請求者
????????????????????((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);
????????????????????if?(logger.isDebugEnabled()) {
????????????????????????logger.debug("Written ["?+ returnValue +?"] as \""?+ selectedMediaType +?"\" using ["?+
????????????????????????????????messageConverter +?"]");
????????????????????}
????????????????}
????????????????return;
????????????}
????????}
????}
????if?(returnValue !=?null) {
????????throw?new?HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
????}
}

接下來,將選擇好的mediatype寫入到HttpOutputMessage中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public?final?void?write(final?T t, MediaType contentType, HttpOutputMessage outputMessage)
????????throws?IOException, HttpMessageNotWritableException {
????final?HttpHeaders headers = outputMessage.getHeaders();?????//設置contenttype到HttpOutputMessage
????if?(headers.getContentType() ==?null) {
????????MediaType contentTypeToUse = contentType;
????????if?(contentType ==?null?|| contentType.isWildcardType() || contentType.isWildcardSubtype()) {
????????????contentTypeToUse = getDefaultContentType(t);
????????}
????????if?(contentTypeToUse !=?null) {
????????????headers.setContentType(contentTypeToUse);
????????}
????}
????if?(headers.getContentLength() == -1) {
????????Long contentLength = getContentLength(t, headers.getContentType());
????????if?(contentLength !=?null) {
????????????headers.setContentLength(contentLength);
????????}
????}
      /* 省略了不相干代碼 */
}

最終的Headers設置在ServletServerHttpResponse類中完成,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private?void?writeHeaders() {
????if?(!this.headersWritten) {
????????for?(Map.Entry<String, List<String>> entry :?this.headers.entrySet()) {
????????????String headerName = entry.getKey();
????????????for?(String headerValue : entry.getValue()) {?????????//將復合類中之前設置的header(content-type)內容補充到servletResponse
????????????????this.servletResponse.addHeader(headerName, headerValue);
????????????}
????????}
????????// HttpServletResponse exposes some headers as properties: we should include those if not already present
????????if?(this.servletResponse.getContentType() ==?null?&&?this.headers.getContentType() !=?null) {
????????????this.servletResponse.setContentType(this.headers.getContentType().toString());
????????}
????????if?(this.servletResponse.getCharacterEncoding() ==?null?&&?this.headers.getContentType() !=?null?&&
????????????????this.headers.getContentType().getCharSet() !=?null) {
????????????this.servletResponse.setCharacterEncoding(this.headers.getContentType().getCharSet().name());
????????}
????????this.headersWritten =?true;
????}
}

從上述的代碼中,我們可以看到在RequestResponseBodyMethodProcessor這個ReturnValueHandler中,media-type被單獨的邏輯進行處理,因此直接在ServletResponse中設置content-type header并不能正常生效。

需要在@RequestMapping中添加produces = {} 進行設置才可以。

轉載于:https://www.cnblogs.com/unknows/p/8724942.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/166664.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息