概述
【原创文章,转载请注明原文章地址,谢谢!】
统一异常处理是所有的web应用都需要考虑的。在Jersey中,也提供了很简单的统一异常处理方式。
Jersey中的异常处理
Web应用的异常处理基本思路,服务层隐藏底层的Checked Exception,服务层的异常统一包装为RuntimeException抛出到Web层,由Web层统一对服务层异常进行处理。常见的两种处理方式,针对Json格式的请求,返回包含异常代码,异常消息或者异常数据的对象返回,针对web页面的请求,统一返回到异常结果展示页面。
WebApplicationException
第一种方式,我们可以利用Jersey本身的异常对象,在资源方法中使用HTTP代码来给客户端返回指定错误。比如直接返回500错误等。
在Jersey中,提供了一个javax.ws.rs.WebApplicationException类,在资源方法中,可以简单的通过返回该异常类,让Jersey统一处理异常返回:
@Path("exception")
public class ExceptionResource {
@POST
@Path("register")
public Response register(@FormParam("name")String username) {
if ("admin".equals(username)) {
throw new WebApplicationException("用户名已经存在!",
Response.Status.CONFLICT);
} else {
return Response.ok("注册成功!", MediaType.TEXT_PLAIN).build();
}
}
}
这里提供了一段代码,exception/register资源方法返回一个Response,我们假设传入一个用户名username,在代码中,我们最简单的模拟了一个业务:如果传入的用户名为admin,则判定为用户名已经存在(在实际的代码中,可能是由业务层返回一个注册成功后的用户对象,如果该用户对象为空,则表明用户名冲突),则直接抛出一个WebApplicationException,在该类的构造方法中,传入了消息内容和响应状态码,这里我们设置的异常消息为:用户名已经存在,使用的状态码为Status.CONFLICT(409);
当我们在POSTMAN中对代码进行测试
如果传入的参数不是admin,则提示注册成功,如果传入的参数为admin:
可以看到服务端正确返回409异常状态码;
再简单介绍一下WebApplicationException:
1,WebApplicationException可以在资源方法中,或者Provider中(拦截器,过滤器,Entity Provider)抛出;
2,WebApplicationException有非常多的构造方法,可以传入不同的参数构造Response;具体的可以看看对应的API文档;
3,使用WebApplicationException对于统一异常处理,还是差距很远,最重要的原因在于针对实际的开发,要求客户端针对不同的状态码进行处理,还是比较麻烦。
ExceptionMapper
第二种方式,Jersey提供了ExceptionMapper接口来更方便的根据异常类型来执行对应的异常处理方法。
我们先来看看ExceptionMapper接口声明:
public interface ExceptionMapper<E extends Throwable> {
Response toResponse(E exception);
}
在该接口中,定义了Response toResponse(E exception);方法,很容易理解,针对匹配的exception类型,怎么去生成一个对应的Response对象。
在我们具体构建我们自己的异常处理之前,我们先来看看第三方框架中做好的示例代码。在Jackson框架中,如果在JSON->实体对象的映射过程中,出现解析错误,Jackson都会抛出一个com.fasterxml.jackson.databind.JsonMappingException异常,注意一下,这个异常是一个checked exception,因为这个异常属于框架级别异常(集成IOException)。
接着,Jackson为我们提供了一个专门用于处理JsonMappingException的错误处理,我们就从这个类开始学习:
public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
@Override
public Response toResponse(JsonMappingException exception) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(exception.getMessage())
.type("text/plain").build();
}
}
首先我们看到,JsonMappingExceptionMapper实现了ExceptionMapper接口,在接口的泛型类型中只针对JsonMappingException进行处理。在实现的toResponse方法中,使用Response构造了Status.BAD_REQUEST(400状态码),并传入异常消息返回。
要使用ExceptionMapper,也只需要添加@Provider注解或者通过ResourceConfig.register方法注册即可。
我们简单介绍一种统一处理方法。首先确定目标结果,我们就针对Json的数据格式。其次,针对整个应用,我们构建一个基础的异常类,再针对不同的服务错误,创建不同的异常子类。在ExceptionMapper中,统一将错误包装为响应对象返回,下面简单展示一些代码:
首先我们创建一个异常类型枚举类,用来表示不同的应用异常类型和对应的异常状态码:
@Getter
public enum ExceptionCode {
DEFAULT_ERROR(0),
PERMISSION_EXCEPTION(1),
MONEY_CHECK_EXCEPTION(2),
ACCOUNT_STATUS_ERROR(3);
private ExceptionCode(int code) {
this.code = code;
}
private int code;
}
接着,创建一个应用的基础异常类:
@Getter
public abstract class ApplicationException extends RuntimeException {
private static final long serialVersionUID = 1L;
private ExceptionCode code = ExceptionCode.DEFAULT_ERROR;
public ApplicationException(String msg) {
super(msg);
}
public ApplicationException(String msg, ExceptionCode code) {
super(msg);
this.code = code;
}
public ApplicationException(String msg, ExceptionCode code,
Throwable cause) {
super(msg, cause);
this.code = code;
}
}
在该基础异常类中,额外保存了一个异常类型;接着针对不同的服务,提供不同的异常子类,比如针对权限访问的异常:
public class PermissionException extends ApplicationException {
private static final long serialVersionUID = 1L;
public PermissionException(String msg, Throwable ex) {
super(msg, ExceptionCode.PERMISSION_EXCEPTION, ex);
}
}
代码很简单,仅仅只是额外规定了异常状态类型为ExceptionCode.PERMISSION_EXCEPTION;
接着创建自己的ExceptionMapper:
@Provider
public class ApplicationExceptionMapper
implements ExceptionMapper<ApplicationException> {
@Override
public Response toResponse(ApplicationException exception) {
AjaxResult result = new AjaxResult(false, exception.getMessage(), null,
exception.getCode().getCode());
return Response.ok(result, MediaType.APPLICATION_JSON).build();
}
}
在该方法中,我们使用拦截到的ApplicationException,构建了一个AjaxResult对象。AjaxResult对象是我们应用对于客户端统一的返回对象:
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class AjaxResult {
private boolean success;//请求是否执行成功
private String msg;//本次请求的消息
private Object data;//本次请求可能携带的内容对象
private int code;//如果出现异常,code代表异常类型码
}
最后,我们来写一个测试资源方法测试我们的异常处理:
@GET
@Path("resource")
@Produces(MediaType.APPLICATION_JSON)
public AjaxResult doSomething(@HeaderParam("token") String token) {
if ("token".equals(token)) {
return new AjaxResult(true, "正常访问资源", "some logic value", 0);
} else {
throw new PermissionException("没有权限访问该资源", null);
}
}
在这里我们的演示资源方法是一个很简单的测试,我们直接判断请求头中是否存在token字段,如果没有,我们直接抛出一个PermissionException异常。
简单的测试(正常的访问):
错误的访问:
小结
在本节中,我们重点介绍了使用ExceptionMapper来做异常的统一处理,额外的,在Jersey中还提供了一个扩展异常处理接口ExtendedExceptionMapper,提供了更灵活的异常和异常处理器的匹配,关于这个接口,建议大家可以去看看API文档。
下一节,我们将介绍Jersey和Spring以及SpringBoot的集成开发。
最后
以上就是细心面包为你收集整理的Jersey 开发RESTful(十六) Jersey统一异常处理的全部内容,希望文章能够帮你解决Jersey 开发RESTful(十六) Jersey统一异常处理所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复