使用 ExceptionHandler 處理業務流程的異常情況

都在說這是啥,我來告訴你怎麼用!

Problem

在業務實現過程中,我們可能會有類似如下的場景,要實現一個下單功能,除了下單正常執行的流程之外,我們還可能需要處理各種的業務異常情況,比如說下單的用戶在服務端找不到,下單的商品在服務端找不到等等的業務異常情況(注意是業務異常),這時可能會有如下的僞代碼,用戶找不到時,給客戶端響應 http status 爲 500,http body 裏的內容爲一個 map 對象;如果是正常執行完畢,則給客戶端的響應爲 http status 200,http body 裏的內容爲訂單的id。

//controller
@RequestMapping
public Object place(HttpServletResponse response,String userId,OrderDTO orderDTO) {
	Map<String,Object> result = new HashMap<>();
	Uzer uzer = orderService.findById(userId);

	if (ObjectUtils.isEmpty(uzer)) {
		result.put("msg", String.format("User %s not be found",userId));
		response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.ordinal());
		return result;
	}
	
	response.setStatus(HttpStatus.OK.ordinal());
	String orderId = orderService.place(userId, orderDTO);
	return orderId;
} 

//service
public String place(String userId, OrderDTO orderDTO) {
	// 正常邏輯
	Uzer uzer = userRepository.findById(userId);
	// 正常邏輯	
	Order order = new Order();
	//assembler form OrderDto
	Order o = orderRepository.save(order);
	return o.id;
}

上面的代碼特別醜,比如用了 Spring MVC ,還在方法上傳入了 HttpServletResponse 對象;因爲不同的情況返回的內容不同,返回值只能是Object,Java的強類型在此一無是處;

Spring MVC Support

如上例所示,一個業務流程的正常情況和異常情況,返回給客戶端的響應碼,響應體的格式都是不同的;那有沒有一種方法可以分開返回業務正常執行成功之後的響應內容和業務的各種異常情況下的響應內容呢?
Spring MVC 的 Exception Handler 機制,完美的解決了這個問題。

//controller
@RequestMapping(value="/place")
@ResponseStatus(code=HttpStatus.CREATED)
public String place(String userId, OrderDTO orderDTO) {
	String orderId = orderService.place(userId, orderDTO);
	return orderId;
}

//service
public String place(String userId, OrderDTO orderDTO) {
	// 正常邏輯
	Uzer uzer = userRepository.findById(userId);

	if (ObjectUtils.isEmpty(uzer)) {
		throw new PlaceOrderException(String.format("User %s is not found", userId));
	}

	// 正常邏輯
	Order order = new Order();
	//assembler form OrderDto
	Order o = orderRepository.save(order);
	return o.id;
}

// exception handler
@ExceptionHandler
@ResponseStatus(value = HttpStatus.OK)
public DRuntimeException dealException(PlaceOrderException e) {
	DRuntimeException exception = new DRuntimeException(e);
	return exception;
}

上述僞代碼解耦了下單這個流程的正常業務邏輯和異常業務結果的處理。在 service 層,如果遇到業務異常的情況,只拋出異常,不做其他任何處理。Spring MVC 捕獲此異常,並回調 Exception Handler 的方法並將此異常傳給此異常處理的邏輯。
解耦之後,在 controller 層,只管構建成功執行的結果,標註的 @ResponseStatus 指定 http 響應的影響碼,controller 的返回結果指定響應的內容。
在 ExceptionHandler 中處理異常的情況,可以給此異常,單獨指定 ResponseStatus 和 響應內容;
如果業務的異常情況想控制的更精細,可以定義更多的異常類,用於代表不同的異常情況,然後就可以給不同的 ExceptionHandler 寫單獨的處理邏輯,指定各自的響應碼和響應體。

More

@ExceptionHandler 是 Web 應用裏使用的,如果輸入輸出是消息中間件,Spring Messaging 模塊還定義了 @MessageExceptionHandler 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章