SpringBoot中的内容协商器图解

网友投稿 294 2023-03-11


SpringBoot中的内容协商器图解

背景

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

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

public abstract class AbstractRestController {

protected Class voClazz;

@Autowired

private Service service;

public AbstractRestController() {

TypeToken voType = new TypeToken(getClass()) {

};

voClazz = (Class) 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 list = service.findAll();

PageInfo pageInfo = new PageInfo(list);

excelExportParam();

return ResultGenerator.genSuccessResult(pageInfo);

}

protected void excelExportParam() {

ExportParams ep = new ExportParams(null, "数据");

ExcelExportParam 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 vos = service.findByIds(ids);

return ResultGenerator.genSuccessResult(vos);

}

@PostMapping("/batch")

@ApiOperation(value = "批量新建实体", notes = "")

public Result add(@RequestBody List 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 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 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 javaNmwBhq.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

implements GenericHttpMessageConverter {

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 list = service.findAll();

PageInfo pageInfo = new PageInfo(list);

excelExportParam();

return ResultGenerator.genSuccessResult(pageInfo);

}

protected void excelExportParam() {

ExportParams ep = new ExportParams(null, "数据");

ExcelExportParam param = new ExcelExportParam<>();

param.setClazz(voClazz);

param.setExcelExport(ExcelExport.NormalExcel);

param.setExportParams(ep);

param.setFileName("文件.xls");

F6Static.setExcelExportParam(param);

}

当我们访问时如下

http://127.0.0.1:8079/zeus/user

{

"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

如下效果

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

总结

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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:企业接口管理平台(企业数据接口平台)
下一篇:微服务和api网关(微服务api网关 aws sp api)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~