springboot项目创建笔记13 之《接口返回统一格式》

一、对所有Controller的response做统一格式封装
我们的接口会返回空、返回字符串、返回对象、返回异常(这个后续讲),所以需要统一返回格式

二、方法一
在接口文档,规定外层的格式,比如需要返回4个字段:data、sign、repCode、repMsg
1、建立包com.example.message
2、建立CommonResponse.java

package com.example.message;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@ApiModel("通用返回对象")
@Data
public class CommonResponse {
	
	@ApiModelProperty(value = "数据")
	private String data; //数据
	
	@ApiModelProperty(value = "签名")
	private String sign; //签名
	
	@ApiModelProperty(value = "应答码")
	private String repCode; //应答码
	
	@ApiModelProperty(value = "应答信息")
	private String repMsg; //应答信息
	
	public static CommonResponse succ() {
		CommonResponse response = new CommonResponse();
		response.setRepCode("000000");
		response.setRepMsg("成功");
		return response;
	}

	public static CommonResponse succ(String data) {
		CommonResponse response = new CommonResponse();
		response.setData(data);
		response.setRepCode("000000");
		response.setRepMsg("成功");
		return response;	
	}
	
	public static CommonResponse fail(String repCode, String repMsg) {
		CommonResponse response = new CommonResponse();
		response.setRepCode(repCode);
		response.setRepMsg(repMsg);
		return response;
	}
	
}

3、代码中Controller统一返回值为CommonResponse类型,建立ResponseController.java

package com.example.web;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.domain.Teacher;
import com.example.message.CommonResponse;

import io.swagger.annotations.Api;

@Api(description = "返回值格式测试")
@RestController
@RequestMapping("/response")
public class ResponseController {

	/**
	 * 方法一
	 * @return
	 */
	@GetMapping("/succ")
	public CommonResponse getSucc() {
		return CommonResponse.succ();  //这样写会被其他程序员鄙视
	}
	
}

4、缺点,每一个接口都要手工指定成功、失败

三、方法二
1、使用ResponseBodyAdvice接口和@ControllerAdvice注解
2、ResponseBodyAdvice接口是在Controller执行return之后,response消息转换为http消息返回给浏览器之前,执行的对response的一些处理
3、@ControllerAdvice注解增强Controller的扩展功能
具体是在执行之前还是执行之后的增强,需要实现不同的接口和注解
4、修改ResponseController.java

package com.example.web;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.domain.Teacher;
import com.example.message.CommonResponse;

import io.swagger.annotations.Api;

@Api(description = "返回值格式测试")
@RestController
@RequestMapping("/response")
public class ResponseController {

	/**
	 * 方法一
	 * @return
	 */
	@GetMapping("/succ")
	public CommonResponse getSucc() {
		return CommonResponse.succ();  //这样写会被其他程序员鄙视
	}
	
	/**
	 * 方法二:返回String
	 * @return
	 */
	@GetMapping("/string")
	public String getString() {
		return "abc";
	}
	
	/**
	 * 方法二:无返回
	 */
	@GetMapping("/empty")
	public void getEmpty() {
		
	}
	
	/**
	 * 方法二:返回对象
	 * @return
	 */
	@GetMapping("/teacher")
	public Teacher getTercher() {
		Teacher teacher = new Teacher();
		teacher.setName("张三");
		teacher.setAge("18");
		teacher.setBeginTime("2020");
		teacher.setEndTime("2021");
		return teacher;
	}
}

5、在包com.example.utils下添加JsonUtil.java

package com.example.utils;

import java.io.IOException;

import org.springframework.util.StringUtils;

import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonUtil {
	private static ObjectMapper objectMapper = new ObjectMapper();

	// 对象转字符串
	public static <T> String obj2String(T obj) {
		if (obj == null) {
			return null;
		}
		try {
			return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	// 字符串转对象
	public static <T> T string2Obj(String str, Class<T> clazz) {
		if (StringUtils.isEmpty(str) || clazz == null) {
			return null;
		}
		try {
			return objectMapper.readValue(str, clazz);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
}

6、在包com.example.utils下添加ResponseHandler.java

package com.example.utils;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import com.example.message.CommonResponse;

@ControllerAdvice(basePackages = "com.example.web")
public class ResponseHandler implements ResponseBodyAdvice<Object> {

	/**
	 * 是否支持advice功能
	 * true=支持,false=不支持
	 */
	@Override
	public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
		return true;
	}

	/**
	 * 处理response的具体方法
	 */
	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
			Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
			ServerHttpResponse response) {
		if (body instanceof String) {
			//需要返回的类型是String
			return JsonUtil.obj2String(CommonResponse.succ((String)body));
		} else if (body == null) {
			return CommonResponse.succ();
		} else {
			//将对象转成json串
			return CommonResponse.succ(JsonUtil.obj2String(body));
		}
		//返回失败由抛异常捕获
	}

}

7、说明
1)@ControllerAdvice(basePackages = "com.example.web")标识了增强功能,basePackages指定了Controller的路径
2)ResponseBodyAdvice拦截Controller方法的返回值,统一处理返回值
3)supports方法,定义了返回值是否需要执行beforeBodyWrite
4)beforeBodyWrite方法,具体对response的操作
5)有response自然也有RequestBodyAdvice接口
6)执行机制:
RequestBodyAdvice:需在Controller层中的方法上加上@RequestMapping和@RequestBody;若是没有@RequestBody,RequestBodyAdvice不会执行
ResponseBodyAdvice:需在controller层中的方法上加上@RequestMapping和ResponseBody;若是没有@ResponseBody,ResponseBodyAdvice不会执行

四、测试
1、执行getEmpty

{
  "data": null,
  "sign": null,
  "repCode": "000000",
  "repMsg": "成功"
}

2、执行getString

{"data":"abc","sign":null,"repCode":"000000","repMsg":"成功"}

在ResponseHandler.java中这样写:

if (body instanceof String) {
	return CommonResponse.succ((String)body);
}

会报错:

{
  "timestamp": "2020-08-30T18:02:20.325+0000",
  "status": 500,
  "error": "Internal Server Error",
  "message": "com.example.message.CommonResponse cannot be cast to java.lang.String",
  "path": "/response/string"
}

因为方法的返回值是String,在增强里面返回的是CommonResponse对象,所以报错了。原因是,返回值类型为String的时候,springboot会默认定义一个StringHttpMessageConverter消息转换器。而body进来是abc,经过拦截处理后返回是CommonResponse对象,无法转换消息

而返回为空和返回对象,消息转换器均为MappingJackson2HttpMessageConverter
3、执行getTercher

{
  "data": "{\"name\":\"张三\",\"age\":\"18\",\"beginTime\":\"2020\",\"endTime\":\"2021\"}",
  "sign": null,
  "repCode": "000000",
  "repMsg": "成功"
}

注:最新代码上传至https://github.com/csj50/myboot
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值