服务器之家

服务器之家 > 正文

SpringBoot中的内容协商器图解

时间:2021-02-23 11:11     来源/作者:波波维奇

背景

使用了restful的小伙伴对于导出这些需求本能就是拒绝的~破坏了restful的url的一致性【严格矫正 不是http json就是restful 很多小伙伴都会吧暴露出一个json就直接称为restful 】

正如上文的代码生成器 我们会批量生成一堆代码 其中绝大部分都是restcontroller

?
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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头里面也能看出

SpringBoot中的内容协商器图解

1.比如给英语客户返回英语页面 过于客户返回汉语页面

http 协议中定义了质量值(简称 q 值),允许客户端为每种偏好类别列出多种选项,并为每种偏好选项关联一个优先次序。

?
1
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   

?
1
2
3
4
5
6
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中采用上述两种方案

首先配置内容协商器

代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@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);
  }

创建对应的转换器

?
1
2
3
4
private httpmessageconverter<object> createexcelhttpmessageconverter() {
  excelhttpmessageconverter excelhttpmessageconverter = new excelhttpmessageconverter();
  return excelhttpmessageconverter;
}

直接使用easy-poi导出数据

?
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * 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);
  }
}

暂时只是针对导出 因此在使用的时候如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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);
 }

当我们访问时如下

 

SpringBoot中的内容协商器图解

?
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
{
 "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?format=xls 或者http://127.0.0.1:8079/zeus/user.xls

如下效果

SpringBoot中的内容协商器图解

SpringBoot中的内容协商器图解

由于这边的数据和查询有关 因此我们可以这样操作http://127.0.0.1:8079/zeus/user.xls?pagesize=1000 轻而易举实现了查询结果xls化!

SpringBoot中的内容协商器图解

SpringBoot中的内容协商器图解

总结

以上所述是小编给大家介绍的springboot中的内容协商器图解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!

原文链接:http://www.jianshu.com/p/f7b257585d9a

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
返回顶部