加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

SpringBoot中的内容协商器图解

发布时间:2020-12-14 20:56:00 所属栏目:Java 来源:网络整理
导读:背景 使用了restful的小伙伴对于导出这些需求本能就是拒绝的~破坏了restful的url的一致性【严格矫正 不是http json就是restful 很多小伙伴都会吧暴露出一个json就直接称为restful 】 正如上文的代码生成器 我们会批量生成一堆代码 其中绝大部分都是RestContr

背景

使用了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/xml;q=0.9,*/*;q=0.8      
    IE8: 
    Accept:image/gif,image/jpeg,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,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,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);
  }

当我们访问时如下

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,"pkId": 24201883434356532,"username": "007"
  },{
  "cellPhone": "15715139000","idEmployee": 24351585207523460,"idOwnOrg": 24201883434357600,"idRole": 89,"idWxbStation": "540","idWxbUser": "298","pkId": 24201883434357600,"username": "15715139000"
  },"idRole": 216,"pkId": 24201883434357920,"username": "sunlingli"
  },"idEmployee": 24351585207425676,"idOwnOrg": 24201883434359384,"idRole": 90,"idWxbStation": "348","idWxbUser": "227","openid": "opzUDs_v13WE500kxYMj6Xg_gFeE","pkId": 24201883434359388,"username": "15952920979"
  },"idOwnOrg": 24201883434359790,"idRole": 91,"idWxbStation": "315","idWxbUser": "175","pkId": 24201883434359790,"username": "13809056211"
  },{
  "cellPhone": "18903885585","idEmployee": 24201883434366164,"idOwnOrg": 24201883434359890,"idRole": 92,"idWxbStation": "317","idWxbUser": "178","pkId": 24201883434359892,"username": "18903885585"
  },"idEmployee": 24351585207425668,"idOwnOrg": 24201883434359924,"idRole": 93,"idWxbStation": "318","idWxbUser": "179","pkId": 24201883434359930,"username": "13372299595"
  },"idOwnOrg": 24201883434360052,"idRole": 94,"idWxbStation": "321","idWxbUser": "188","pkId": 24201883434360052,"username": "15221250005"
  },"idOwnOrg": 24201883434360070,"idRole": 95,"idWxbStation": "325","idWxbUser": "198","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中的内容协商器图解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程小技巧网站的支持!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读