接着上一篇来说,

不管正常返回结果还是后台出现异常,应该返回给前台统一的响应格式。

所以这一篇就为了应对解决这个问题。

========================================================================

1.首先,定义一个统一返回类【所有返回的格式都是这个类的格式】

package com.sxd.sweeping.response;

import com.alibaba.fastjson.JSON;
import lombok.*; import java.io.Serializable;
import java.util.Objects; /**
* 统一JSON返回类
* @author sxd
* @since 2018/4/1
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class GenericResponse implements Serializable{ /**
* 程序定义状态码
*/
private int code;
/**
* 必要的提示信息
*/
private String message;
/**
* 业务数据
*/
private Object datas; /**
* 对业务数据单独处理
* @return
*/
@Override
public String toString() {
if(Objects.isNull(this.datas)){
this.setDatas(new Object());
}
return JSON.toJSONString(this);
}
}

2.定义统一返回数据格式【这里定义了一些常用返回code+msg,用于填充上面的统一格式】

package com.sxd.sweeping.response;

import com.google.common.collect.Maps;

import java.util.Map;

/**
* 统一返回客户端数据格式
* @author sxd
* @since 2018/4/1
*/
public class ResponseFormat { private static Map<Integer,String> messageMap = Maps.newHashMap();
//初始化状态码与文字说明
static {
/* 成功状态码 */
messageMap.put(200, "成功"); /* 服务器错误 */
messageMap.put(1000,"服务器错误"); /* 参数错误:10001-19999 */
messageMap.put(10001, "参数无效");
messageMap.put(10002, "参数为空");
messageMap.put(10003, "参数类型错误");
messageMap.put(10004, "参数缺失"); /* 用户错误:20001-29999*/
messageMap.put(20001, "用户未登录");
messageMap.put(20002, "账号不存在或密码错误");
messageMap.put(20003, "账号已被禁用");
messageMap.put(20004, "用户不存在");
messageMap.put(20005, "用户已存在"); /* 业务错误:30001-39999 */
messageMap.put(30001, "某业务出现问题"); /* 系统错误:40001-49999 */
messageMap.put(40001, "系统繁忙,请稍后重试"); /* 数据错误:50001-599999 */
messageMap.put(50001, "数据未找到");
messageMap.put(50002, "数据有误");
messageMap.put(50003, "数据已存在");
messageMap.put(50004,"查询出错"); /* 接口错误:60001-69999 */
messageMap.put(60001, "内部系统接口调用异常");
messageMap.put(60002, "外部系统接口调用异常");
messageMap.put(60003, "该接口禁止访问");
messageMap.put(60004, "接口地址无效");
messageMap.put(60005, "接口请求超时");
messageMap.put(60006, "接口负载过高"); /* 权限错误:70001-79999 */
messageMap.put(70001, "无权限访问");
}
public static GenericResponse retParam(Integer status,Object data) {
GenericResponse json = new GenericResponse(status, messageMap.get(status), data);
return json;
}
}

3.定义自己的异常类 因为spring 对于 RuntimeException 异常才会进行事务回滚,所以继承的是RuntimeException【可用于代码中自己throw异常】

package com.sxd.sweeping.handler;

import lombok.Getter;
import lombok.Setter; /**
* spring 对于 RuntimeException 异常才会进行事务回滚。
* @author sxd
* @since 2018/4/1
*/
@Getter
@Setter
public class MyException extends RuntimeException { public MyException(Integer code, Exception exception) {
this.code = code;
this.exception = exception;
} private Integer code;
private Exception exception;
}

4.定义Controller增强器,使用注解@ControllerAdvice,具体使用说明查看代码【对异常进行统一拦截,然后配置返回格式】

package com.sxd.sweeping.handler;

import com.sxd.sweeping.response.GenericResponse;
import com.sxd.sweeping.response.ResponseFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*; /**
* controller 增强器 原理是使用AOP对Controller控制器进行增强(前置增强、后置增强、环绕增强)
* 启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都会作用在 被 @RequestMapping 注解的方法上。
* @ModelAttribute:在Model上设置的值,对于所有被 @RequestMapping 注解的方法中,都可以通过 ModelMap获取,或者通过@ModelAttribute("author")也可以获取
* @ExceptionHandler 拦截了异常,我们可以通过该注解实现自定义异常处理。其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型,下面拦截了 Exception.class 这种异常。
* @author sxd
* @since 2018/4/1
*/
@ControllerAdvice
public class MyControllerAdvice { Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
* @param binder
*/
@InitBinder
public void initBinder(WebDataBinder binder) {} /**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
* @param model
*/
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("author", "sxd");
} /**
* 全局异常捕捉处理
* @param ex
* @return
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public GenericResponse errorHandler(Exception ex) {
ex.printStackTrace();
return ResponseFormat.retParam(1000,null);
} /**
* 拦截捕捉自定义异常 MyException.class
* @param ex
* @return
*/
@ResponseBody
@ExceptionHandler(value = MyException.class)
public GenericResponse myErrorHandler(MyException ex) {
ex.getException().printStackTrace();
logger.error(ex.getException().toString());
return ResponseFormat.retParam(ex.getCode(),null);
}
}

5.Swagger类

package com.sxd.sweeping;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration
@EnableSwagger2
public class Swagger2 { @Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.sxd.sweeping.controller"))
.paths(PathSelectors.any())
.build();
} private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Spring Boot中使用Swagger2构建RESTful APIs")
.description("更多精彩博客请关注:http://www.cnblogs.com/sxdcgaq8080/")
.termsOfServiceUrl("http://www.cnblogs.com/sxdcgaq8080/")
.contact("Angel挤一挤")
.version("1.0")
.build();
}
}

6.

  6.1 Controller类

package com.sxd.sweeping.controller;

import com.sxd.sweeping.entity.User;
import com.sxd.sweeping.handler.MyException;
import com.sxd.sweeping.repository.UserRepository;
import com.sxd.sweeping.response.GenericResponse;
import com.sxd.sweeping.response.ResponseFormat;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*; import java.sql.Date;
import java.util.UUID; @Api(value = "userController",description = "用户相关操作",tags = {"用户"})
@RestController
@RequestMapping("users")
public class UserController { @Autowired
private UserRepository userRepository; @ApiOperation(value = "获取用户详细信息", notes = "根据url的id来获取用户的详细信息")
@ApiImplicitParam(name = "id", value = "用户ID", required = true,dataType = "Long",paramType = "path")
// @RequestMapping(value = "/{id}",method = RequestMethod.GET)
@GetMapping(value = "/{id}")
public GenericResponse get(@PathVariable Long id){
User user;
try{
user = userRepository.findUserById(id);
}catch(Exception e){
user = null;
throw new MyException(50004,e);
}
return ResponseFormat.retParam(200,user);
} @ApiOperation(value = "增加用户", notes = "根据user对象创建用户")
@ApiImplicitParam(name = "user", value = "用户详细信息User", required = true,dataType = "User")
// @RequestMapping(method = RequestMethod.POST)
@PostMapping()
public GenericResponse add(@RequestBody User user){
String password = UUID.randomUUID().toString();
user.setPassword(password);
user = userRepository.save(user); return ResponseFormat.retParam(200,user);
} @ApiOperation(value = "删除用户", notes = "根据url的id来删除用户")
@ApiImplicitParam(name = "id", value = "用户ID", required = true,dataType = "Long",paramType = "path")
// @RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
@DeleteMapping(value = "/{id}")
@ResponseBody
public GenericResponse delete(@PathVariable Long id){
userRepository.deleteById(id);
return ResponseFormat.retParam(200,"ID为"+id+"的用户删除成功");
} @ApiOperation(value = "更新用户", notes = "根据url的id来更新用户信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long", paramType = "path"),
@ApiImplicitParam(name = "user", value = "用户实体user", required = true,dataType = "User")
})
// @RequestMapping(value = "/{id}",method = RequestMethod.PUT)
@PutMapping(value = "/{id}")
public GenericResponse update(@PathVariable Long id, @RequestBody User user){
User user1 = userRepository.findUserById(id);
user1.setGender(user.getGender());
user1.setMobile(user.getMobile());
user1.setRealname(user.getRealname());
user1.setUpdateAt(new Date(System.currentTimeMillis())); return ResponseFormat.retParam(200,user1);
} @ApiOperation(value = "更新用户局部信息", notes = "根据url的id来更新用户局部信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long", paramType = "path"),
@ApiImplicitParam(name = "realname", value = "用户名", required = true,dataType = "String"),
@ApiImplicitParam(name = "mobile", value = "联系方式", required = true,dataType = "String")
})
// @RequestMapping(value = "/{id}",method = RequestMethod.PATCH)
@PatchMapping(value = "/{id}")
public GenericResponse patch(@PathVariable Long id, String realname, String mobile){
User user = userRepository.findUserById(id);
user.setRealname(realname);
user.setMobile(mobile); return ResponseFormat.retParam(200,userRepository.saveAndFlush(user));
} @ApiOperation(value = "获取用户信息列表", notes = "获取用户信息列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "页码", required = true,dataType = "int"),
@ApiImplicitParam(name = "size", value = "单页条数", required = true,dataType = "int")
})
@GetMapping()
public GenericResponse list(
@RequestParam(name = "page",defaultValue = "1") int page,
@RequestParam(name = "size",defaultValue = "10") int size){
Pageable pageable = new PageRequest(page-1,size);
Page<User> pageList = userRepository.findAll(pageable);
return ResponseFormat.retParam(200,pageList);
} @ApiOperation(value = "筛选获取用户信息列表", notes = "筛选获取用户信息列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "key", value = "关键字", required = true,dataType = "String"),
@ApiImplicitParam(name = "startDate", value = "开始时间", required = true,dataType = "Date"),
@ApiImplicitParam(name = "endDate", value = "结束时间", required = true,dataType = "Date"),
@ApiImplicitParam(name = "page", value = "页码", required = true,dataType = "int"),
@ApiImplicitParam(name = "size", value = "单页条数", required = true,dataType = "int")
})
@GetMapping(value = "/pageList")
public GenericResponse pageList(
@RequestParam(name = "key") String key,
@RequestParam(name = "startDate") Date startDate,
@RequestParam(name = "endDate") Date endDate,
@RequestParam(name = "page",defaultValue = "1") int page,
@RequestParam(name = "size",defaultValue = "10") int size){
Pageable pageable = new PageRequest(page-1,size);
Page<User> pageList = userRepository.pageList(key,startDate,endDate,pageable);
return ResponseFormat.retParam(200,pageList);
} }

  6.2 Repository接口

package com.sxd.sweeping.repository;

import com.sxd.sweeping.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import java.sql.Date; public interface UserRepository extends JpaRepository<User,Long> { User findUserById(Long id); @Query(value = "select u from User u " +
"where :keyword is null or u.realname like %:keyword% " +
"and (u.updateAt between :startDate and :endDate)")
Page<User> pageList(
@Param("keyword") String keyword,
@Param("startDate") Date startDate,
@Param("endDate") Date endDate,
Pageable pageable);
}

7.对于异常处理,异常日志的记录,所以还需要配置logback日志配置

<?xml version="1.0" encoding="UTF-8"?>

<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <contextName>logback</contextName>
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="log.path" value="/Users/sxd/IdeaProjects/A/sweeping.log" /> <!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<!--<pattern>%d %p (%file:%line\)- %m%n</pattern>-->
<!--格式化输出:%d:表示日期 %thread:表示线程名 %-5level:级别从左显示5个字符宽度 %msg:日志消息 %n:是换行符-->
<pattern>%black(控制台-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger) - %cyan(%msg%n)</pattern>
<charset>UTF-8</charset>
</encoder>
</appender> <!--输出到文件-->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logback.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<!--格式化输出:%d:表示日期 %thread:表示线程名 %-5level:级别从左显示5个字符宽度 %msg:日志消息 %n:是换行符-->
<pattern>文件记录-%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender> <root level="info">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root> </configuration>

8.application.properties文件

spring.datasource.url = jdbc:postgresql://127.0.0.1:54320/ctgs
spring.datasource.username = homestead
spring.datasource.password = secret
spring.datasource.driver-class-name = org.postgresql.Driver spring.jpa.database=postgresql
spring.jpa.show-sql=true debug=true

9.build.gradle依赖

buildscript {
ext {
springBootVersion = '2.0.0.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
} apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management' group = 'com.sxd'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8 repositories {
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
maven { url 'https://repo.spring.io/plugins-release' }
maven { url 'https://jitpack.io' }
mavenCentral()
} dependencies {
compile('org.springframework.boot:spring-boot-starter-activemq')
compile('org.springframework.boot:spring-boot-starter-amqp')
compile('org.springframework.boot:spring-boot-starter-aop')
compile('org.springframework.boot:spring-boot-starter-cache')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-data-mongodb')
compile('org.springframework.boot:spring-boot-starter-data-redis')
compile('org.springframework.boot:spring-boot-starter-data-solr')
compile('org.springframework.boot:spring-boot-starter-freemarker')
compile('org.springframework.boot:spring-boot-starter-jdbc')
compile('org.springframework.boot:spring-boot-starter-validation')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.2')
runtime('com.microsoft.sqlserver:mssql-jdbc')
runtime('mysql:mysql-connector-java')
runtime('org.postgresql:postgresql')
compileOnly('org.projectlombok:lombok')
testCompile('org.springframework.boot:spring-boot-starter-test') // https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.8.0'
// https://mvnrepository.com/artifact/io.springfox/springfox-swagger2
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.8.0'
// https://mvnrepository.com/artifact/com.alibaba/fastjson
compile group: 'com.alibaba', name: 'fastjson', version: '1.2.47' }

最后 启动项目,访问地址:http://localhost:8080/swagger-ui.html#/

请求操作

正常情况下请求:

错误情况下:

查看日志文件:

【swagger】2.swagger提供开发者文档--返回统一格式篇【spring mvc】【spring boot】的更多相关文章

  1. 【swagger】1.swagger提供开发者文档--简单集成到spring boot中【spring mvc】【spring boot】

    swagger提供开发者文档 ======================================================== 作用:想使用swagger的同学,一定是想用它来做前后台 ...

  2. Core Web API上使用Swagger提供API文档

    在ASP.NET Core Web API上使用Swagger提供API文档   我在开发自己的博客系统(http://daxnet.me)时,给自己的RESTful服务增加了基于Swagger的AP ...

  3. Swagger 生成 PHP API 接口文档

    Swagger 生成 PHP API 接口文档 Lumen微服务生成Swagger文档 1.概况 有同学反馈写几十个接口文档需要两天的工作量, 随着多部门之间的协作越来越频繁, 维护成本越来越高, 文 ...

  4. 使用Swagger实现webapi接口自动化文档生成

    这里是实现自动化api稳当的生成,在网上看了很多swagger的文档,可能都是在为实现接口时直接使用的swagger,其实步骤差不多,但是更加详细的我还没看到,又或者说,我看着文档来的时候还是出错啦, ...

  5. layuiAdmin pro v1.x 【单页版】开发者文档

    layuiAdmin std v1.x [iframe版]开发者文档 题外 该文档适用于 layuiAdmin 专业版(单页面),阅读之前请务必确认是否与你使用的版本对应. 熟练掌握 layuiAdm ...

  6. layuiAdmin std v1.x 【iframe版】开发者文档

    layuiAdmin pro v1.x [单页版]开发者文档 layuiAdmin.std(iframe 版) 是完全基于 layui 架构而成的通用型后台管理模板系统,采用传统的 iframe 多页 ...

  7. 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理

    服务器文档下载zip格式   刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...

  8. developers.google.com上的开发者文档如何切换显示语言

    一个小的tip,搜索到developers.google.com上的开发者文档,有些被翻译了的会自动显示中本版,如果想看英文版,可以在当前url后面加?hl=en,就会变成英文版.估计是根据地区直接推 ...

  9. Typora+PicGo+cos图床打造开发者文档神器

    一.Typora简介 markdown简单.高效的语法,被每一个开发者所喜爱.Typora又是一款简约.强悍的实时渲染markdown编辑器.本文将介绍Typora搭配PicGo与腾讯cos对象存储( ...

随机推荐

  1. Linux学习-RPM 软件管理程序: rpm

    RPM 默认安装的路径 一般来说,RPM 类型的文件在安装的时候,会先去读取文件内记载的设定参数内容,然后将该数据用来比对 Linux 系统的环境,以找出是否有属性相依的软件尚未安装的问题. 若环境检 ...

  2. 算法学习记录-查找——折半查找(Binary Search)

    以前有个游戏,一方写一个数字,另一方猜这个数字.比如0-100内一个数字,看谁猜中用的次数少. 这个里面用折半思想猜会大大减少次数. 步骤:(加入数字为9) 1.因为数字的范围是0-100,所以第一次 ...

  3. LoadRunner web_set_sockets_option()--常用函数

    web_set_sockets_option()--常用函数 设置sockets的选项. intweb_set_sockets_option(const char * option,const cha ...

  4. 面向对象——property

    1.property特性 property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值 将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉到name是执行了一 ...

  5. 缓存淘汰算法之LFU

    1. LFU类 1.1. LFU 1.1.1. 原理 LFU(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频 ...

  6. Pandas对多列进行升降序排列

    df = pd.DataFrame(rows, columns = ["llx", "lly", "urx", "ury" ...

  7. [整理]配置SSH密钥自动登录远程服务器

    原理: 公钥私钥匹配通过验证,允许访问服务器. 简单步骤: 1.在本地创建一对密钥 2.将公钥传到需要访问的服务器上 3.将公钥放入服务器的authorized_keys,确保访问时能通过验证 4.本 ...

  8. rabbitmq exchange type

    This is the fourth installment to the series: RabbitMQ for Windows.  In thelast installment, we revi ...

  9. 九度oj 题目1368:二叉树中和为某一值的路径

    题目描述: 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径. 输入: 每个测试案例包括n+1行: 第一行为2 ...

  10. iOS长按手势调用两次解决方法

    由于以前没有很细致的研究过长按手势,所以今天使用的时候发现长按手势会调用两次响应事件. 主要原因是长按手势会分别在UIGestureRecognizerStateBegan和UIGestureReco ...