接着上一篇來講,html
無論正常返回結果仍是後臺出現異常,應該返回給前臺統一的響應格式。java
因此這一篇就爲了應對解決這個問題。mysql
========================================================================web
1.首先,定義一個統一返回類【全部返回的格式都是這個類的格式】redis
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,用於填充上面的統一格式】spring
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異常】sql
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,具體使用說明查看代碼【對異常進行統一攔截,而後配置返回格式】mongodb
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類json
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.api
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#/
請求操做
正常狀況下請求:
錯誤狀況下:
查看日誌文件: