SpringBoot中的内容协商器图解
背景
使用了restful的小伙伴对于导出这些需求本能就是拒绝的~破坏了restful的url的一致性【严格矫正 不是http json就是restful 很多小伙伴都会吧暴露出一个json就直接称为restful 】
正如上文的代码生成器 我们会批量生成一堆代码 其中绝大部分都是restcontroller
public abstract class abstractrestcontroller<v extends vo, s extends so, pk extends serializable> { protected class<v> voclazz; @autowired private service<v, s, pk> service; public abstractrestcontroller() { typetoken<v> votype = new typetoken<v>(getclass()) { }; voclazz = (class<v>) votype.getrawtype(); } @postmapping() @apioperation(value = "新建实体", notes = "") public result add(@requestbody v vo) { service.saveselective(vo); return resultgenerator.gensuccessresult(); } @deletemapping("/{id}") @apioperation(value = "删除实体", notes = "") public result delete(@pathvariable pk id) { service.deletebyid(id); return resultgenerator.gensuccessresult(); } @putmapping @apioperation(value = "更新实体", notes = "") public result update(@requestbody v vo) { service.updatebyprimarykeyselective(vo); return resultgenerator.gensuccessresult(); } @getmapping @apioperation(value = "获取实体列表", notes = "") public result list(s so) { pagehelper.startpage(so.getcurrentpage(), so.getpagesize()); list<v> list = service.findall(); pageinfo pageinfo = new pageinfo(list); excelexportparam(); return resultgenerator.gensuccessresult(pageinfo); } protected void excelexportparam() { exportparams ep = new exportparams(null, "数据"); excelexportparam<v> param = new excelexportparam<>(); param.setclazz(voclazz); param.setexcelexport(excelexport.normalexcel); param.setexportparams(ep); param.setfilename("文件.xls"); f6static.setexcelexportparam(param); } @getmapping("/{id}") @apioperation(value = "获取单个实体", notes = "") public result detail(@pathvariable pk id) { v vo = service.findbyid(id); return resultgenerator.gensuccessresult(vo); } @deletemapping("/batch") @apioperation(value = "批量删除实体", notes = "") public result batchdelete(@requestparam string ids) { service.deletebyids(ids); return resultgenerator.gensuccessresult(); } @getmapping("/batch") @apioperation(value = "批量获取实体", notes = "") public result batchdetail(@requestparam string ids) { list<v> vos = service.findbyids(ids); return resultgenerator.gensuccessresult(vos); } @postmapping("/batch") @apioperation(value = "批量新建实体", notes = "") public result add(@requestbody list<v> vos) { service.save(vos); return resultgenerator.gensuccessresult(); } @getmapping("/count") @apioperation(value = "获取实体数目", notes = "") public result count(@requestbody v v) { int count = service.selectcount(v); return resultgenerator.gensuccessresult(count); }
那么导出如何做呢?【其实可以理解成导出就是数据的展示 不过此时结果不是json而已】
抛出一个问题那么登录登出呢?传统的方案都是login logout 那么换成restful资源的思路是啥呢?
提示: 登录就是session的新建 登出就是session的删除
实现
基于上述思路 我们自然就想到了那么我们只需要对同一个url返回多种结果不就ok了?【pdf一个版本 json一个版本 xml一个版本 xls一个版本】
bingo!这个是内容协商器的由来
内容协商器并不是spring创造出来的 事实上这个从http头里面也能看出
1.比如给英语客户返回英语页面 过于客户返回汉语页面
http 协议中定义了质量值(简称 q 值),允许客户端为每种偏好类别列出多种选项,并为每种偏好选项关联一个优先次序。
accept-language: en;q=0.5, fr;q=0.0, nl;q=1.0, tr;q=0.0
其中 q 值的范围从 0.0 ~ 1.0(0.0 是优先级最低的,而 1.0 是优先级最高的)。
注意,偏好的排列顺序并不重要,只有与偏好相关的 q 值才是重要的
2.那么还有其他的一些参数 比如 accept-header
通常是先内容协商器有如下几种方案
1.使用accept header:
这一种为教科书中通常描述的一种,理想中这种方式也是最好的,但如果你的资源要给用户直接通过浏览器访问(即html展现),那么由于浏览器的差异,发送上来的accept header头将是不一样的. 将导致服务器不知要返回什么格式的数据给你. 下面是浏览器的accept header
chrome: accept:application/xml,application/xhtml+xml,textml;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 firefox: accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 ie8: accept:image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/x-silverlight, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
2.使用扩展名
丧失了同一url多种展现的方式,但现在这种在实际环境中是使用最多的.因为更加符合程序员的审美观.
比如/user.json /user.xls /user.xml
使用参数 现在很多open api是使用这种方式,比如淘宝
但是对于不同浏览器可能accept-header并不是特别统一 因此许多实现选择了2 3两种方案
我们在spring中采用上述两种方案
首先配置内容协商器
代码
@bean public viewresolver contentnegotiatingviewresolver( contentnegotiationmanager manager) { // define the view resolvers viewresolver beannameviewresolver = new beannameviewresolver(); list<viewresolver> resolvers = lists.newarraylist(beannameviewresolver); contentnegotiatingviewresolver resolver = new contentnegotiatingviewresolver(); resolver.setviewresolvers(resolvers); resolver.setcontentnegotiationmanager(manager); return resolver; } @override public void configurecontentnegotiation(contentnegotiationconfigurer configurer) { configurer.favorpathextension(true) .usejaf(false) .favorparameter(true) .parametername("format") .ignoreacceptheader(true) .defaultcontenttype(mediatype.application_json) .mediatype("json", mediatype.application_json) .mediatype("xls", excel_media_type); }
创建对应的转换器
private httpmessageconverter<object> createexcelhttpmessageconverter() { excelhttpmessageconverter excelhttpmessageconverter = new excelhttpmessageconverter(); return excelhttpmessageconverter; }
直接使用easy-poi导出数据
/* * copyright (c) 2017. lorem ipsum dolor sit amet, consectetur adipiscing elit. * morbi non lorem porttitor neque feugiat blandit. ut vitae ipsum eget quam lacinia accumsan. * etiam sed turpis ac ipsum condimentum fringilla. maecenas magna. * proin dapibus sapien vel ante. aliquam erat volutpat. pellentesque sagittis ligula eget metus. * vestibulum commodo. ut rhoncus gravida arcu. */ package com.f6car.base.web.converter; import cn.afterturn.easypoi.excel.excelexportutil; import com.f6car.base.common.result; import com.f6car.base.core.excelexport; import com.f6car.base.core.excelexportparam; import com.github.pagehelper.pageinfo; import com.google.common.collect.lists; import org.apache.poi.ss.usermodel.workbook; import org.springframework.http.httpheaders; import org.springframework.http.httpinputmessage; import org.springframework.http.httpoutputmessage; import org.springframework.http.mediatype; import org.springframework.http.converter.abstracthttpmessageconverter; import org.springframework.http.converter.generichttpmessageconverter; import org.springframework.http.converter.httpmessagenotreadableexception; import org.springframework.http.converter.httpmessagenotwritableexception; import java.io.ioexception; import java.lang.reflect.type; import java.net.urlencoder; import java.util.collection; import java.util.collections; import java.util.map; import static com.f6car.base.core.f6static.getexcelexportparam; /** * @author qixiaobo */ public class excelhttpmessageconverter extends abstracthttpmessageconverter<object> implements generichttpmessageconverter<object> { public static final mediatype excel_media_type = new mediatype("application", "vnd.ms-excel"); public excelhttpmessageconverter() { super(excel_media_type); } @override protected boolean supports(class<?> clazz) { return false; } @override protected object readinternal(class<?> clazz, httpinputmessage inputmessage) throws ioexception, httpmessagenotreadableexception { return null; } @override protected void writeinternal(object o, httpoutputmessage outputmessage) throws ioexception, httpmessagenotwritableexception { httpheaders headers = outputmessage.getheaders(); collection data = getactualdata((result) o); excelexportparam excelexportparam = getexcelexportparam(); workbook workbook; switch (excelexportparam.getexcelexport()) { case normalexcel: workbook = excelexportutil.exportexcel( excelexportparam.getexportparams(), (class<?>) excelexportparam.getclazz(), (collection<?>) data); break; case mapexcel: workbook = excelexportutil.exportexcel( excelexportparam.getexportparams(), excelexportparam.getexcelexportentities(), (collection<? extends map<?, ?>>) data); break; case bigexcel: case mapexcelgraph: case pdftemplate: case templateexcel: case templateword: default: throw new runtimeexception(); } if (workbook != null) { if (excelexportparam.getfilename() != null) { string codedfilename = urlencoder.encode(excelexportparam.getfilename(), "utf8"); headers.setcontentdispositionformdata("attachment", codedfilename); } workbook.write(outputmessage.getbody()); } } private collection getactualdata(result r) { if (r != null && r.getdata() != null) { object data = r.getdata(); if (data instanceof pageinfo) { return ((pageinfo) data).getlist(); } else if (!(data instanceof collection)) { data = lists.newarraylist(data); } else { return (collection) data; } } return collections.emptylist(); } @override public boolean canread(type type, class<?> contextclass, mediatype mediatype) { //不支持excel return false; } @override public object read(type type, class<?> contextclass, httpinputmessage inputmessage) throws ioexception, httpmessagenotreadableexception { return null; } @override public boolean canwrite(type type, class<?> clazz, mediatype mediatype) { return super.canwrite(mediatype) && clazz == result.class && support(); } private boolean support() { excelexportparam param = getexcelexportparam(); if (param == null || param.getexcelexport() == null || param.getexportparams() == null) { return false; } if (param.getexcelexport() == excelexport.normalexcel) { return true; } else { logger.warn(param.getexcelexport() + " not supprot now!"); return false; } } @override public void write(object o, type type, mediatype contenttype, httpoutputmessage outputmessage) throws ioexception, httpmessagenotwritableexception { super.write(o, contenttype, outputmessage); } }
暂时只是针对导出 因此在使用的时候如下
@getmapping @apioperation(value = "获取实体列表", notes = "") public result list(s so) { pagehelper.startpage(so.getcurrentpage(), so.getpagesize()); list<v> list = service.findall(); pageinfo pageinfo = new pageinfo(list); excelexportparam(); return resultgenerator.gensuccessresult(pageinfo); } protected void excelexportparam() { exportparams ep = new exportparams(null, "数据"); excelexportparam<v> param = new excelexportparam<>(); param.setclazz(voclazz); param.setexcelexport(excelexport.normalexcel); param.setexportparams(ep); param.setfilename("文件.xls"); f6static.setexcelexportparam(param); }
当我们访问时如下
{ "code": 200, "data": { "endrow": 10, "firstpage": 1, "hasnextpage": true, "haspreviouspage": false, "isfirstpage": true, "islastpage": false, "lastpage": 8, "list": [ { "cellphone": "13857445502", "idemployee": 24201883434352650, "idownorg": 23993199378825296, "idrole": 88, "idwxbstation": "332", "idwxbuser": "207", "isadmin": 1, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 23993199378825296, "username": "lingweiqiche" }, { "cellphone": "", "idemployee": 0, "idownorg": 9999, "idrole": 4, "idwxbstation": "", "idwxbuser": "", "isadmin": 0, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 24201883434356532, "username": "007" }, { "cellphone": "15715139000", "idemployee": 24351585207523460, "idownorg": 24201883434357600, "idrole": 89, "idwxbstation": "540", "idwxbuser": "298", "isadmin": 1, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 24201883434357600, "username": "15715139000" }, { "cellphone": "", "idemployee": 0, "idownorg": 24201883434357600, "idrole": 216, "idwxbstation": "", "idwxbuser": "", "isadmin": 0, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 24201883434357920, "username": "sunlingli" }, { "cellphone": "", "idemployee": 24351585207425676, "idownorg": 24201883434359384, "idrole": 90, "idwxbstation": "348", "idwxbuser": "227", "isadmin": 1, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "opzuds_v13we500kxymj6xg_gfee", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 24201883434359388, "username": "15952920979" }, { "cellphone": "", "idemployee": 0, "idownorg": 24201883434359790, "idrole": 91, "idwxbstation": "315", "idwxbuser": "175", "isadmin": 1, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 24201883434359790, "username": "13809056211" }, { "cellphone": "18903885585", "idemployee": 24201883434366164, "idownorg": 24201883434359890, "idrole": 92, "idwxbstation": "317", "idwxbuser": "178", "isadmin": 1, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 24201883434359892, "username": "18903885585" }, { "cellphone": "", "idemployee": 24351585207425668, "idownorg": 24201883434359924, "idrole": 93, "idwxbstation": "318", "idwxbuser": "179", "isadmin": 1, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 24201883434359930, "username": "13372299595" }, { "cellphone": "", "idemployee": 0, "idownorg": 24201883434360052, "idrole": 94, "idwxbstation": "321", "idwxbuser": "188", "isadmin": 1, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 24201883434360052, "username": "15221250005" }, { "cellphone": "", "idemployee": 0, "idownorg": 24201883434360070, "idrole": 95, "idwxbstation": "325", "idwxbuser": "198", "isadmin": 1, "isdel": 0, "isguideopen": 0, "limitmac": 0, "openid": "", "password": "96e79218965eb72c92a549dd5a330112", "pkid": 24201883434360070, "username": "13837251167" } ], "navigatefirstpage": 1, "navigatelastpage": 8, "navigatepages": 8, "navigatepagenums": [ 1, 2, 3, 4, 5, 6, 7, 8 ], "nextpage": 2, "orderby": "", "pagenum": 1, "pagesize": 10, "pages": 102, "prepage": 0, "size": 10, "startrow": 1, "total": 1012 }, "message": "success" }
当访问 或者
如下效果
由于这边的数据和查询有关 因此我们可以这样操作http://127.0.0.1:8079/zeus/user.xls?pagesize=1000 轻而易举实现了查询结果xls化!
总结
以上所述是小编给大家介绍的springboot中的内容协商器图解,希望对大家有所帮助
上一篇: java编程实现并查集的路径压缩代码详解
下一篇: Java 实现随机验证码功能简单实例
推荐阅读
-
SpringBoot中的内容协商器图解
-
SpringBoot的拦截器中依赖注入为null的解决方法
-
在WordPress的文章编辑器中设置默认内容的方法_php实例
-
【SpringBoot】六、SpringBoot中拦截器功能的实现
-
在HTML中利用js调用php的内容-计数器实例
-
在WordPress的文章编辑器中设置默认内容的方法_php实例
-
SpringBoot 之 SpringMVC拦截器从Request中获取参数并解决request的请求流只能读取一次的问题
-
搜狐影音播放器中的dlna功能使用方法图解
-
怎么把用fckeditor编辑好的新闻内容再次完整地读入到编辑器中修改
-
在HTML中利用js调用php的内容-计数器实例