欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot中的内容协商器图解

程序员文章站 2024-02-26 15:04:58
背景 使用了restful的小伙伴对于导出这些需求本能就是拒绝的~破坏了restful的url的一致性【严格矫正 不是http json就是restful 很多小伙伴都会...

背景

使用了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头里面也能看出

SpringBoot中的内容协商器图解

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);
  }

当我们访问时如下

SpringBoot中的内容协商器图解

 {
  "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"
  }

当访问 或者

如下效果

SpringBoot中的内容协商器图解

SpringBoot中的内容协商器图解

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

SpringBoot中的内容协商器图解

SpringBoot中的内容协商器图解

总结

以上所述是小编给大家介绍的springboot中的内容协商器图解,希望对大家有所帮助