前后端分离项目处理异常最佳实践方法推荐(vue, Java)

如何处理异常是项目中极为头痛的一件事,尤其是在前后端分离的项目中,Exception必须作为Restful来处理,这里包括如何避免处理Exception的代码分散在项目代码,这样对于异常处理的重构和多语言支持都会造成很大的麻烦;还包括如何正确定义异常信息,使得用户所看到的错误提示信息是有效的,而不是一些数据库的error-code, 或者是500的HTTP STATUS。

当异常能够作为JSON成功传递到前端,这时又涉及到前端代码如何解析异常,将异常所包含的信息显示给用户,下面就给出后端Java的异常生成和传递,以及前端VUE接收到异常后处理方法。

我们要实现的结果是前端接收到如下JSON消息,并把消息的内容显示给用户

{"exception":"DuplicatedUserException","error":"Conflict","message":"admin 已经被占用. ","status":409,"errors":null}

从消息内容可以看出这是一个提示用户将要注册的用户名已经被占用,HttpStatus_Conflict: 409, 异常是用户自定义的DuplicatedUserException.

后端Java代码:

定义一个抽象类包含ErrorResponse的生成,将来其他每一个自定义异常都对应的Handler都会继承这个抽象类, 

import java.util.Collection;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;

import com.lims.api.exception.ErrorResponse;

/**
 * Extend this to code an exception handler
 */
public abstract class AbstractExceptionHandler<T extends Throwable> {
	
	protected final Log log = LogFactory.getLog(this.getClass());
	
	private String exceptionName;
	
	public AbstractExceptionHandler(String exceptionName) {
		this.exceptionName = exceptionName;
	}

	public String getExceptionName() {
		return exceptionName;
	}
	
	protected String getMessage(T ex) {
		return ex.getMessage();
	}
	
	protected HttpStatus getStatus(T ex) {
		return null;
	}
	
	protected Collection<FieldError> getErrors(T ex) {
		return null;
	}

	public ErrorResponse getErrorResponse(T ex) {
		
		ErrorResponse errorResponse = new ErrorResponse();
		
		String message = getMessage(ex);
		if (message != null)
			errorResponse.setMessage(message);
		
		HttpStatus status = getStatus(ex);
		if (status != null) {
			errorResponse.setStatus(status.value());
			errorResponse.setError(status.getReasonPhrase());
		}
		
		errorResponse.setErrors(getErrors(ex));
		
		return errorResponse;
	}
}

自定义异常类:DuplicatedUserException, 异常的消息通过property文件给定,便于消息内容更改和多语言支持

import org.springframework.http.HttpStatus;

import com.lims.api.util.LimsUtils;

public class DuplicatedUserException extends RuntimeException {

	/**
	 * 
	 */
	private static final long serialVersionUID = -21168497361531088L;
	// HTTP Status code to be returned
	private HttpStatus status = HttpStatus.BAD_REQUEST;

	public DuplicatedUserException(String userName) {
		super(LimsUtils.getMessage("com.lims.api.exception.DuplicatedUserException", userName));
	}

	public HttpStatus getStatus() {
		return status;
	}

	public void setStatus(HttpStatus status) {
		this.status = status;
	}

	public DuplicatedUserException httpStatus(HttpStatus status) {
		this.status = status;
		return this;
	}

}

自定义异常类的Handler

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import com.lims.api.exception.AbstractExceptionHandler;
import com.lims.api.security.web.exception.DuplicatedUserException;

@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class DuplicatedUserExceptionHandler extends AbstractExceptionHandler<DuplicatedUserException> {

	public DuplicatedUserExceptionHandler() {
		
		super(DuplicatedUserException.class.getSimpleName());
		log.info("DuplicatedUserExceptionHandler Created");
	}
	

	@Override
	public HttpStatus getStatus(DuplicatedUserException ex) {
		return ex.getStatus();
	}

}

基于以上三个类,所有@Controller中所抛出的异常就可以被包含到Response中传递到前端。以下示例代码:

    @PostMapping("/sign-up")
    public void signUp(@RequestBody ApplicationUser user){
        if(!applicationUserService.isValid(user)) {
        	throw (new DuplicatedUserException(user.getUserName()).httpStatus(HttpStatus.CONFLICT));
        };
        user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        applicationUserRepository.save(user);
    }

前端VUE大多数会使用Axios,这里也用它来示例:

 vm.$ajax.post('/api/users/sign-up', register)
            .then(function (res) {
              toastr.success(res.data.message)
            }).catch(function (error) {
              toastr.error(error.response.data.message)
            })

这样前后端的异常处理在结构上显得十分简洁,每一个自定义的异常都可以显示给用户更友好的信息,通过property文件对于信息的定义可以充分利用属性文件的多语言支持功能。

前端处理异常的代码无需任何条件处理,每一个异常的消息显示都是统一的代码。对于前端代码模块重用有着重要的作用。

Cheers!

猜你喜欢

转载自blog.csdn.net/java_augur/article/details/81276151