javascript
一篇文章教你弄懂springmvc中的httpmessageconverter -尊龙游戏旗舰厅官网
文章目录
- 一、httpmessageconverter介绍
- 二、自定义httpmessageconverter
写在前面:
我是「境里婆娑」。我还是从前那个少年,没有一丝丝改变,时间只不过是考验,种在心中信念丝毫未减,眼前这个少年,还是最初那张脸,面前再多艰险不退却。
写博客的目的就是分享给大家一起学习交流,如果您对 java感兴趣,可以关注我,我们一起学习。
导语:相信使用过spring的开发人员都用过@requestbody、@responsebody注解,可以直接将输入解析成json、将输出解析成json,但http 请求和响应是基于文本的,意味着浏览器和服务器通过交换原始文本进行通信,而这里其实就是httpmessageconverter发挥着作用。
一、httpmessageconverter介绍
http请求响应报文其实都是字符串,当请求报文到java程序会被封装为一个servletinputstream流,开发人员再读取报文,响应报文则通过servletoutputstream流,来输出响应报文。
从流中只能读取到原始的字符串报文,同样输出流也是。那么在报文到达springmvc / springboot出去,都存在一个字符串到java对象的转化问题。这一过程,在springmvc / springboot中,是通过httpmessageconverter来解决的。
httpmessageconverter接口提供了5个方法:
- canread:判断该转换器是否能将请求内容转换成java对象
- canwrite:判断该转换器是否可以将java对象转换成返回内容
- getsupportedmediatypes:获得该转换器支持的mediatype类型
- read:读取请求内容并转换成java对象
- write:将java对象转换后写入返回内容
其中read和write方法的参数分别有有httpinputmessage和httpoutputmessage对象,这两个对象分别代表着一次http通讯中的请求和响应部分,可以通过getbody方法获得对应的输入流和输出流。
其中read和write方法的参数分别有有httpinputmessage和httpoutputmessage对象,这两个对象分别代表着一次http通讯中的请求和响应部分,可以通过getbody方法获得对应的输入流和输出流。
缺省配置
平常我们在发送http请求时,没有配置任何 messageconverter,但是数据前后传递依旧好用,是因为 springmvc 启动时会自动配置一些httpmessageconverter,在 webmvcconfigurationsupport 类中添加了缺省 messageconverter:
httpmessageconverter匹配过程
当使用 @requestbody和 @responsebody注解时,requestmappinghandleradapter就使用它们来进行读取或者写入相应格式的数据。
@requestbody 据request对象header部分的content-type类型,逐一匹配合适的httpmessageconverter来读取数据。 底层调用的是requestresponsebodymethodprocessor readwithmessageconverters方法
protected <t> object readwithmessageconverters(httpinputmessage inputmessage, methodparameter parameter,type targettype) throws ioexception, httpmediatypenotsupportedexception, httpmessagenotreadableexception {mediatype contenttype;boolean nocontenttype = false;try {contenttype = inputmessage.getheaders().getcontenttype();}catch (invalidmediatypeexception ex) {throw new httpmediatypenotsupportedexception(ex.getmessage());}if (contenttype == null) {nocontenttype = true;contenttype = mediatype.application_octet_stream;}class<?> contextclass = parameter.getcontainingclass();class<t> targetclass = (targettype instanceof class ? (class<t>) targettype : null);if (targetclass == null) {resolvabletype resolvabletype = resolvabletype.formethodparameter(parameter);targetclass = (class<t>) resolvabletype.resolve();}httpmethod httpmethod = (inputmessage instanceof httprequest ? ((httprequest) inputmessage).getmethod() : null);object body = no_value;emptybodycheckinghttpinputmessage message;try {message = new emptybodycheckinghttpinputmessage(inputmessage);for (httpmessageconverter<?> converter : this.messageconverters) {class<httpmessageconverter<?>> convertertype = (class<httpmessageconverter<?>>) converter.getclass();generichttpmessageconverter<?> genericconverter =(converter instanceof generichttpmessageconverter ? (generichttpmessageconverter<?>) converter : null);if (genericconverter != null ? genericconverter.canread(targettype, contextclass, contenttype) :(targetclass != null && converter.canread(targetclass, contenttype))) {if (message.hasbody()) {httpinputmessage msgtouse =getadvice().beforebodyread(message, parameter, targettype, convertertype);body = (genericconverter != null ? genericconverter.read(targettype, contextclass, msgtouse) :((httpmessageconverter<t>) converter).read(targetclass, msgtouse));body = getadvice().afterbodyread(body, msgtouse, parameter, targettype, convertertype);}else {body = getadvice().handleemptybody(null, message, parameter, targettype, convertertype);}break;}}}catch (ioexception ex) {throw new httpmessagenotreadableexception("i/o error while reading input message", ex, inputmessage);}if (body == no_value) {if (httpmethod == null || !supported_methods.contains(httpmethod) ||(nocontenttype && !message.hasbody())) {return null;}throw new httpmediatypenotsupportedexception(contenttype, this.allsupportedmediatypes);}mediatype selectedcontenttype = contenttype;object thebody = body;logformatutils.tracedebug(logger, traceon -> {string formatted = logformatutils.formatvalue(thebody, !traceon);return "read \"" selectedcontenttype "\" to [" formatted "]";});return body;}@responsebody 底层调用的abstractmessageconvertermethodprocessor
protected <t> void writewithmessageconverters(@nullable t value, methodparameter returntype,servletserverhttprequest inputmessage, servletserverhttpresponse outputmessage)throws ioexception, httpmediatypenotacceptableexception, httpmessagenotwritableexception {object body;class<?> valuetype;type targettype;if (value instanceof charsequence) {body = value.tostring();valuetype = string.class;targettype = string.class;}else {body = value;valuetype = getreturnvaluetype(body, returntype);targettype = generictyperesolver.resolvetype(getgenerictype(returntype), returntype.getcontainingclass());}if (isresourcetype(value, returntype)) {outputmessage.getheaders().set(httpheaders.accept_ranges, "bytes");if (value != null && inputmessage.getheaders().getfirst(httpheaders.range) != null &&outputmessage.getservletresponse().getstatus() == 200) {resource resource = (resource) value;try {list<httprange> httpranges = inputmessage.getheaders().getrange();outputmessage.getservletresponse().setstatus(httpstatus.partial_content.value());body = httprange.toresourceregions(httpranges, resource);valuetype = body.getclass();targettype = resource_region_list_type;}catch (illegalargumentexception ex) {outputmessage.getheaders().set(httpheaders.content_range, "bytes */" resource.contentlength());outputmessage.getservletresponse().setstatus(httpstatus.requested_range_not_satisfiable.value());}}}mediatype selectedmediatype = null;mediatype contenttype = outputmessage.getheaders().getcontenttype();if (contenttype != null && contenttype.isconcrete()) {if (logger.isdebugenabled()) {logger.debug("found 'content-type:" contenttype "' in response");}selectedmediatype = contenttype;}else {httpservletrequest request = inputmessage.getservletrequest();list<mediatype> acceptabletypes = getacceptablemediatypes(request);list<mediatype> producibletypes = getproduciblemediatypes(request, valuetype, targettype);if (body != null && producibletypes.isempty()) {throw new httpmessagenotwritableexception("no converter found for return value of type: " valuetype);}list<mediatype> mediatypestouse = new arraylist<>();for (mediatype requestedtype : acceptabletypes) {for (mediatype producibletype : producibletypes) {if (requestedtype.iscompatiblewith(producibletype)) {mediatypestouse.add(getmostspecificmediatype(requestedtype, producibletype));}}}if (mediatypestouse.isempty()) {if (body != null) {throw new httpmediatypenotacceptableexception(producibletypes);}if (logger.isdebugenabled()) {logger.debug("no match for " acceptabletypes ", supported: " producibletypes);}return;}mediatype.sortbyspecificityandquality(mediatypestouse);for (mediatype mediatype : mediatypestouse) {if (mediatype.isconcrete()) {selectedmediatype = mediatype;break;}else if (mediatype.ispresentin(all_application_media_types)) {selectedmediatype = mediatype.application_octet_stream;break;}}if (logger.isdebugenabled()) {logger.debug("using '" selectedmediatype "', given " acceptabletypes " and supported " producibletypes);}}if (selectedmediatype != null) {selectedmediatype = selectedmediatype.removequalityvalue();for (httpmessageconverter<?> converter : this.messageconverters) {generichttpmessageconverter genericconverter = (converter instanceof generichttpmessageconverter ?(generichttpmessageconverter<?>) converter : null);if (genericconverter != null ?((generichttpmessageconverter) converter).canwrite(targettype, valuetype, selectedmediatype) :converter.canwrite(valuetype, selectedmediatype)) {body = getadvice().beforebodywrite(body, returntype, selectedmediatype,(class<? extends httpmessageconverter<?>>) converter.getclass(),inputmessage, outputmessage);if (body != null) {object thebody = body;logformatutils.tracedebug(logger, traceon ->"writing [" logformatutils.formatvalue(thebody, !traceon) "]");addcontentdispositionheader(inputmessage, outputmessage);if (genericconverter != null) {genericconverter.write(body, targettype, selectedmediatype, outputmessage);}else {((httpmessageconverter) converter).write(body, selectedmediatype, outputmessage);}}else {if (logger.isdebugenabled()) {logger.debug("nothing to write: null body");}}return;}}}if (body != null) {throw new httpmediatypenotacceptableexception(this.allsupportedmediatypes);}}二、自定义httpmessageconverter
一般springboot会有默认的消息管理器,stringhttpmessageconverter(字符串转换器),fastjsonhttpmessageconverter ,mappingjackson2httpmessageconverter等等。
今天我要说的是如何自定义消息管理器-fastjsonhttpmessageconverter
1.引用fastjson依赖
2. 创建配置文件jsonhttpmessageconfiguration.java
@configuration public class myjsonhttpmessageconfiguration {@bean@conditionalonmissingbean(fastjsonhttpmessageconverter.class)public fastjsonhttpmessageconverter fastjsonhttpmessageconverter() {//1.需要定义一个convert转换消息的对象fastjsonhttpmessageconverter fastjsonhttpmessageconverter = new fastjsonhttpmessageconverter();//2.添加fastjson的配置信息;fastjsonconfig config = new fastjsonconfig();//添加配置-config.setserializerfeatures() 方法中可以添加多个配置(以,分开)config.setserializerfeatures(serializerfeature.writemapnullvalue,serializerfeature.disablecircularreferencedetect);//添加配置-config.setserializerfeatures() 方法中可以添加多个配置(以,分开)config.setserializerfeatures(serializerfeature.writemapnullvalue,serializerfeature.disablecircularreferencedetect);fastjsonhttpmessageconverter.setfastjsonconfig(config);fastjsonhttpmessageconverter.setsupportedmediatypes(getsupportedmediatypes());fastjsonhttpmessageconverter.setdefaultcharset(charset.forname("utf-8"));return fastjsonhttpmessageconverter;}/*** 配置全局支持的媒体类型*/private list<mediatype> getsupportedmediatypes() {list<mediatype> supportedmediatypes = new arraylist<>();supportedmediatypes.add(mediatype.application_json);supportedmediatypes.add(mediatype.application_atom_xml);supportedmediatypes.add(mediatype.application_form_urlencoded);supportedmediatypes.add(mediatype.application_octet_stream);supportedmediatypes.add(mediatype.application_pdf);supportedmediatypes.add(mediatype.application_rss_xml);supportedmediatypes.add(mediatype.application_xhtml_xml);supportedmediatypes.add(mediatype.application_xml);supportedmediatypes.add(mediatype.image_gif);supportedmediatypes.add(mediatype.image_jpeg);supportedmediatypes.add(mediatype.image_png);supportedmediatypes.add(mediatype.text_event_stream);supportedmediatypes.add(mediatype.text_html);supportedmediatypes.add(mediatype.text_markdown);supportedmediatypes.add(mediatype.text_plain);supportedmediatypes.add(mediatype.text_xml);return supportedmediatypes;} }serializerfeature配置的属性解释
属性名称含义quotefieldnames | 输出key时是否使用双引号,默认为true |
usesinglequotes | 使用单引号而不是双引号,默认为false |
writemapnullvalue | 是否输出值为null的字段,默认为false应用场景:前端必须需要所有字段 |
useiso8601dateformat | date使用iso8601格式输出,默认为false |
writenulllistasempty | list字段如果为null,输出为[],而不是null |
writenullstringasempty | 字符类型字段如果为null,输出为"",而不是null |
writenullnumberaszero | 数值字段如果为null,输出为0,而非null |
writenullbooleanasfalse | boolean字段如果为null,输出为false,而非null |
skiptransientfield | 如果是true,类中的get方法对应的field是transient,序列化时将会被忽略。默认为true |
sortfield | 按字段名称排序后输出。默认为false |
到此springmvc中的httpmessageconverter介绍完毕。如果还有不明白的可以留言。
—————————————————————————————————
由于本人水平有限,难免有不足,恳请各位大佬不吝赐教!
总结
以上是尊龙游戏旗舰厅官网为你收集整理的一篇文章教你弄懂springmvc中的httpmessageconverter的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: spring中的initializing
- 下一篇: 一篇文章教你弄懂 springmvc中的