REST(三)處理HTTP狀態碼、異常和響應頭
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+"】資訊");
}