REST(三)处理HTTP状态码、异常和响应头
之前的内容只是讨论了正确的处理结果,而没有讨论当没有找到资源时的处理或者发生异常时的处理。当发生资源找不到或者处理逻辑发生异常时,需要考虑的时返回给客户端HTTP抓鬼你太吗和错误消息的问题。为了简化这些开发,Spring提供了实体封装类ResponseEntity
和注解@ResponseStatus
。ResponseEntity
可以有效封装错误消息和状态码,通过@ResponseStatus
可以配置指定的响应码给客户端。
HTTP状态码
在大部分情况下,后台请求成功后会返回一个200的状态码,代表请求成功。但是有时候这些还不够具体,例如,新增了用户,200状态码固然没错,但是使用201会更具体一些。因为201代表着新增资源成功。200只是代表请求成功而已。这时就可以使用@ResponseEntity
类或者@ResponseStatus
来标识本次请求的状态码。除了可以在HTTP响应头中加入属性响应码之外,还可以给响应头加入属性来提供成功或者失败的消息。
下面修改新增用户的方法,将状态码修改为201,并且插入响应头的属性来表示这次请求的结果。
//使用状态码
@PostMapping("/user2/entity")
public ResponseEntity<UserVo> insertUserEntity(@RequestBody UserVo userVo){
User user=this.changeToEntity(userVo);
userService.inserUser(user);
UserVo result=this.changeToVo(user);
HttpHeaders headers=new HttpHeaders();
String success=(result==null||result.getId()==null)?"false":"true";
//设置响应头,比较常用的方法
headers.add("success",success);
//下面是使用集合LIST方式,不常用
//headers.put("success", Arrays.asList(success));
//返回创建成功的状态码
return new ResponseEntity<UserVo>(result,headers, HttpStatus.CREATED);
}
@PostMapping("/user2/annotation")
//指定状态码为201 资源创建成功
@ResponseStatus(HttpStatus.CREATED)
@ResponseBody
public UserVo insertUserAnnotation(@RequestBody UserVo userVo){
User user=this.changeToEntity(userVo);
userService.inserUser(user);
UserVo result=this.changeToVo(user);
return result;
}
方法返回的是一个ResonseEntity<UserVo>
的对象,这里还生成了响应头(HttpHeaders对象)。并且天了手续ingsuccess
来表示请求是否成功,最后返回的时候生成了一个ResonseEntity<UserVo>
对象,然后将查询到的用户对象和响应头捆绑上,并且指定响应码为201。
在insertUserAnnotation
方法上则是使用注解ResponseStatus
讲HTTP的响应码标注为201。所以方法正常返回时将会响应码设置为201。
为了测试,我们写一段js脚本
//测试请求响应码
postStatus()
function postStatus() {
//请求体
var params={
'userName':'user_name_new',
'sexCode':1,
'note':'note_new'
}
//var url='./user2/entity';
var url='./user2/annotation';
$.post({
url:url,
contentType:'application/json',
data:JSON.stringify(params),
success:function (result, status, jqXHR) {
//获取响应头
var success=jqXHR.getResponseHeader("success");
//获取状态吗
var status=jqXHR.status;
alert("响应头参数是:"+success+",状态码是:"+status);
if(result==null){
alert("插入失败");
return;
}else {
alert("插入成功");
}
}
})
}
处理异常
有时候程序会出一些异常,例如,按照编号查找用户,可能查找不到数据,这个时候就不能正常返回去处理了,又或者在执行的过程中产生了异常,这也是需要们进行处理的。
我你可以使用spring mvc注解@ControllerAdvice
和@ExceptionHandler
,@ControllerAdvice
用来定义控制器通知,@ExceptionHandler
则是指定异常发生的处理方法。利用这些就可以处理异常了。
我们先定义一个查询失败的异常
自定义异常类
package com.lay.rest.exception;
/**
* @Description:
* @Author: lay
* @Date: Created in 0:07 2018/11/18
* @Modified By:IntelliJ IDEA
*/
public class NotFoundException extends RuntimeException{
private static final long serialVersionUid=1L;
//异常编码
private Long code;
//异常自定义信息
private String customMsg;
public NotFoundException(){
}
public NotFoundException(Long code,String customMsg){
super();
this.code=code;
this.customMsg=customMsg;
}
public static long getSerialVersionUid() {
return serialVersionUid;
}
public Long getCode() {
return code;
}
public void setCode(Long code) {
this.code = code;
}
public String getCustomMsg() {
return customMsg;
}
public void setCustomMsg(String customMsg) {
this.customMsg = customMsg;
}
}
它继承了RuntimeException
,所以可以在找不到用户的时刻抛出异常。而在控制器抛出异常后,则可以在控制器通知(@Controller)中来处理这些异常,这个时候就需要使用注解@ExceptionHandler
了。
定义控制器通知
package com.lay.rest.exception;
/**
* @Description: 控制器通知
* @Author: lay
* @Date: Created in 0:14 2018/11/18
* @Modified By:IntelliJ IDEA
*/
@ControllerAdvice(
//指定拦截包的控制器
basePackages = {"com.lay.rest.controller.*"},
// 限定为标注为@Controller和@RestController的类才会被拦截
annotations = {Controller.class, RestController.class})
public class VoControllerAdvice {
//异常处理,可以定义异常类型进行拦截处理
@ExceptionHandler(value = NotFoundException.class)
//以json表达式响应
@ResponseBody
//定义服务器错误状态吗
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String,Object> exception(HttpServletRequest request,NotFoundException ex){
Map<String,Object> msgMap=new HashMap<>();
//获取异常信息
msgMap.put("code",ex.getCode());
msgMap.put("message",ex.getCustomMsg());
return msgMap;
}
//异常处理,可以定义异常类型进行拦截处理
@ExceptionHandler(value = RuntimeException.class)
//以json表达式响应
@ResponseBody
//定义服务器错误状态吗
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String,Object> exceptionAll(HttpServletRequest request,Exception ex){
Map<String,Object> msgMap=new HashMap<>();
//获取异常信息
msgMap.put("message",ex.getMessage());
return msgMap;
}
}
这里使用了@ControllerAdvice
来标注类,说明在定义一个控制器通知。配置了它所拦截的包,限定了拦截器的那些被标注为注解@Controller
和@RestController
的控制器。
@ExceptionHandler
定义了拦截器NotFoundException的异常。
测试控制器异常
//测试控制器通知异常处理
@GetMapping(value = "/user/exp/{id}",
//产生Json数据集
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
//响应成功
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public UserVo getUserForExp(@PathVariable("id") Long id){
User user=userService.getUser(id);
//如果找不到用户就抛出异常,进入通知
throw new NotFoundException(1L,"找不到用户【"+id+"】信息");
}