基于Spring Boot的RESTful API实践(一)

时间:2023-03-09 16:28:27
基于Spring Boot的RESTful API实践(一)

1. RESTful简述
    REST是一种设计风格,是一组约束条件及原则,而遵循REST风格的架构就称为RESTful架构,资源是RESTful的核心,一个好的RESTful架构,通过URL就能很清晰的了解其相应的操作和需求是什么,即
1. 通过URL定位资源,如:
com.mobin/api/v1/shenzhen/subways //获取深圳地铁列表
com.mobin/api/v1/shenzhen/schools   //获取深圳学校列表
2. HTTP描述操作
GET:获取资源
POST:创建资源
PUT:更新资源
DELETE:删除资源
如:
GET com.mobin/api/v1/shenzhen/subways/1:获取ID为1的地铁信息
请求的资源通常都以JSON或XML等返回给Client,使用RESTful架构只需在Sever端提供一套RESTful API便能在web、IOS、Android共用
API设计是个很讲究的事情,可以参考GitHub的API设计规范

2. SpringBoot集成MyBatis
 1. 将xxxMapper.xml及sqlMapConfig.xml放入resource对应目录
 2. application.properties加载MyBatis配置文件及配置数据源

 ##application.properties
#加载mybatis配置文件
mybatis.mapper-locations = classpath:mapper/*Mapper.xml
mybatis.config-location = classpath:mapper/config/sqlMapConfig.xml

#指定别名
mybatis.type-aliases-package = com.mobin.entity

#数据源
spring.datasource.url = jdbc:postgresql://localhost:5432/xxx
spring.datasource.driver-class-name = org.postgresql.Driver
spring.datasource.username = postgres
spring.datasource.password = xxxx

MyBatis 3之后官方提供了特定的API来的支持动态SQL,可以使用注解+Java代码来替代XML,但是复杂的SQL语句还是走XML的方式,XML更直观(其实也恶心),注解的方式对于代码的侵入太严重,修改后还需要重新编译。

3. 日志配置
     Spring boot默认的日志框架为Logback,配置起来相当友好具有更好的性能,还可以对指定的服务的日志进行不同级别的过滤并不影响其他服务日志的级别
1. 控制台日志配置
2. 开发、测试、生产环境的配置

 ##logbcak-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<contextName>${appname}</contextName>
<!--控制台日志配置-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread]%-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>

<appender name="FILEPROD" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>SpringBootRESTful.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- 日志保留天数-->
<MaxHistory>20</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread]%-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>

<appender name="FILETEST" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>test.SpringBootRESTful.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>20</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread]%-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>

<!--开发环境的日志配置 -->
<springProfile name="dev">
<!--开发环境通常会对指定包或类进行测试 -->
<logger name="com.mobin.dao" level="DEBUG">
<appender-ref ref="STDOUT"/>
</logger>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</springProfile>

<!--测试环境的日志配置 -->
<springProfile name="test">
<root level="DEBUG">
<appender-ref ref="FILETEST"/>
<appender-ref ref="STDOUT"/>
</root>
</springProfile>

<!--生产环境的日志配置 -->
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="FILEINFO"/>
<appender-ref ref="STDOUT"/>
</root>
</springProfile>
</configuration>

4. 异常处理
4.1 在API设计规范中,对异常的处理至少是
 1. 返回相应的错误代码
 2. 返回相应的错误描述
 3. 对应的资源模块
4.2 使用@RestControllerAdvice定义全局异常处理
@RestControllerAdvice是Spring 4.3之后的新特性,其相当于@ControllerAdvice+ResponseBody,所以使用@RestControllerAdvice可以将错误信息以Json的格式返回
1. 自定义异常类

 ##com.mobin.exception.EntityNotFoundException
//当请求的资源不存在时抛出该异常
public class EntityNotFoundException extends RuntimeException{
public EntityNotFoundException(String mes){
super(mes);
}
}

2. 定义全局异常类

 ##com.mobin.exception.GlobalExceptionHadlerActice
@RestControllerAdvice
public class GlobalExceptionHadlerActice {
private static final long serialVersionUID = 1L;
@ExceptionHandler(value = EntityNotFoundException.class)
public ErrorMessage entityNotFoundException(HttpServletRequest request, Exception e){
ErrorMessage errorMessage = new ErrorMessage();
errorMessage.setStatus(HttpStatus.NOT_FOUND.value());
errorMessage.setMessage(e.getLocalizedMessage());
errorMessage.setUrl(request.getRequestURL().toString());
return errorMessage;
}
}

ErrorMessage为自定义的实体类,包含statusCode,message及url字段。

3. 相应的请求方法

 ## com.mobin.controller.SubwayController
@RequestMapping(value="/{id}",method = RequestMethod.GET )
public SubwayResult<Subway> getSubwayByID(@PathVariable Integer id) {
SubwayResult<Subway> result = new SubwayResult();
Subway subway = subwayService.findSubwayByID(id);
if (subway == null){
throw new EntityNotFoundException("资源不存在");
}
result.setStatus(HttpStatus.OK.value());
result.setData(subway);
return result;
}

4. 通过curl进行测试

 MOBIN:~ mobin$ curl -XGET -w "\n" 'localhost:8089/api/subways/1999'
{
"message":"资源不存在",
"status":404,
"url":"http://localhost:8089/api/subways/1999"
}

5. 使用fastjson
1.  引入fastJson依赖
2. 自定义WebMvcConfigurer并继承WebMvcConfigurerAdapter
3. 重写configureMessageConverters方法
4. 自定义配置FastJsonConfig
5. 将FastJsonHttpMessageConverter添加到HttpMessageConverter
相应代码:

 ##com.mobin.config.WebMvcConfigurer
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter{
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.WriteNullBooleanAsFalse,
SerializerFeature.PrettyFormat);
converter.setDateFormat("yyyy-MM-dd HH:mm:ss");
converter.setFastJsonConfig(config);
converters.add(converter);
}
}

SerializerFeature.WriteNullListAsEmpty:List类型字段为null时输出[]而非null
SerializerFeature.WriteMapNullValue:显示空字段
SerializerFeature.WriteNullStringAsEmpty:字符串类型字段为null时间输出""而非null
SerializerFeature.WriteNullBooleanAsFalse:Boolean类型字段为null时输出false而null
SerializerFeature.PrettyFormat:美化json输出,否则会作为整行输出

解决浏览器中返回json显示乱码问题(参考自:http://blog.****.net/kingboyworld/article/details/70068676

##application.properties
spring.http.encoding.charset=UTF-8
spring.http.encoding.enable=true
spring.http.encoding.force=true

6. 使用PageHelper进行分页
      分页使用的是PageHelper插件,基本原理就是在Executor及mappedStatement之间对SQL语句进行拦截并对SQL添加相应的分页操作再封装传递给mappedStatement,该插件支持单表及多表的分页,使用方便,只需在SQL执行语句前加上一条分布代码即可(通常是在server层),想想如果是手动的设置SQL语句的limit和offset,分页场景一旦多了就特别恶心,即便配合MyBatis的逆向工程也是。
1. 引入Spring boot对应的PageHelper依赖
2. 在application.properties配置PageHelper
3. 在指定的SQL语句前添加分页代码

 ##application.properties
#指定数据库方言
pagehelper.helperDialect=postgresql
#pageNum<=0时返回第一页数据,pageNum超过总页数时返回最后一页数据
pagehelper.reasonable=true

相应代码:

 ##com.mobin.service.impl.SubwayServiceImpl
public List<Subway> findSubways(int pageNum,int pageSize){
//第三个参数为fales时表示不对表进行count操作
PageHelper.startPage(pageNum,pageSize,false);
return subwayMapper.findSubways();
}

项目地址:

https://github.com/MOBIN-F/SpringBootRESTful

参考资料:

GitHub API

How to use @RestControllerAdvice for handling Exception with RestfulApi

PageHelper使用方法

SerializerFeature使用详解

logback相关配置