2014-09-05 85 views
2

我正在为我的REST服务设计一个异常类层次结构,并面临以下问题。 请注意,解决方案必须与Java 7兼容。REST服务的异常层次结构

假设我正在构建一个管理考试的系统。比方说,我有以下资源:

  • 学生(studentId,名字,姓氏,...)
  • 课程(courseId,姓名,...)
  • 考试(examId,等级。 ..)

和以下操作(其中包括):

  • GET /学生/ {} studentId
  • GET /场/ {courseId}
  • POST /学生/ {studentId} /考试(与包含的档次和courseId体)

注意,对于一些操作courseId作为一个部分提供的URL和其他作为请求主体的一部分。

我想设计一个异常类层次结构以一致的方式向客户端报告错误。我想对每个异常类的以下属性:

  • 的StatusCode - 为对响应的HTTP状态代码,
  • 的errorCode - 错误的唯一标识符,
  • 的errorMessage - 人类可读的描述的错误。

现在让我们来关注“无效ID”类错误。我有以下要求:

  • 如果无效ID被作为URL的一部分设置,的StatusCode应该是404(未找到)。如果它是作为请求主体的一部分提供的,那么它应该是400(错误请求)。

  • errorCode/errorMessage应该从业务角度确定错误。 errorCode应该是一个能够唯一标识错误原因的数字(假设为1代表invalid-studentId,2代表invalid-courseId等),errorMessage应该描述人类可重写格式的原因(例如“ID为Student [135]不存在。“)

的问题是,我怎么这两个ortogonal层次合并成一个单一异常类层次结构?

理想我想有follwing异常类:

public abstract class ApiException extends Exception { 

    public abstract int getStatusCode(); 
    public abstract int getErrorCode(); 
    public abstract String getErrorMessage(); 
} 

public abstract class NotFoundException extends ApiException { 

    public int getStatusCode() { 
     return 404; 
    } 
} 

public abstract class BadRequestException extends ApiException { 

    public int getStatusCode() { 
     return 400; 
    } 
} 

public abstract class InvalidCourseIdException extends ApiException { 

    private final String courseId; 

    public InvalidCourseIdException(final String courseId) { 
     this.courseId = courseId; 
    } 

    public int getErrorCode() { 
     return 2; 
    } 

    public String getErrorMessage() { 
     return "Course with ID [" + courseId + "] does not exist."; 
    } 
} 

public class CourseNotFoundException extends NotFoundException, InvalidCourseException { 
    public CourseNotFoundException(String courseId) { 
     super(courseId); 
    } 
} 

public class BadCourseException extends BadRequestException, InvalidCourseException { 

    public BadCourseException(String courseId) { 
     super(courseId); 
    } 
} 

... 

当然,多重继承不可用Java编写的。我如何设计一个兼容Java 7的类层次结构,遵守DRY原则(我希望只在一个地方定义每个常量值)?

+0

我认为你需要在这里看一下KISS原则而不是DRY。从开发角度来看,我希望能够快速查找是什么导致了异常,或者当我抛出一个异常时会发生什么。我不想拖拉一个复杂的层次来做到这一点。 – tom 2014-09-05 15:10:39

回答

1

检查异常的关键方面是你可以捕获它们并从状态恢复。

例外不应按名称传输原因。在400或404的情况下,不可能恢复,所以应用程序应记录状态和中断。

ApiException类更能够启动。

如果你害怕产生大量的代码克隆。您可以创建一个util类来收集所有案例。

final class ApiThrowables { 

     static final int BAD_REQUEST = 400; 
     static final int NOT_FOUND = 404; 

     public static ApiException newCourseIdNotFound(String courseId) { 
      return new ApiException(2, NOT_FOUND,"Course with ID [" + courseId + "] does not exist."); 
     } 

     public static ApiException newBadCourseId(String courseId) { 
      return new ApiException(2, BAD_REQUEST,"Course ID [" + courseId + "] is not valid."); 
     } 

    } 

后来,当你将开发应用程序,你将可能会改变例外的设计,这种成分可以让你做到这一点。当你使用继承而不是组合时,你的代码变成了继电器对偶,这就抑制了改变和重用的潜力。

1

不要将太多的信息放入Exception类。具有您提到的属性的ExamRestServiceException完全可以。如果您想禁止某些属性组合,请使构造函数保持私有状态并使用工厂方法,例如

... 
private ExamRestServiceException(int httpStatusCode, int errorCode, int objectId, String message) { 
    // initialize your exception here 
} 

public ExamRestServiceException of(int httpStatusCode, int errorCode, int objectId, String message) { 
    // check the arguments here 
    return new ExamRestServiceException(httpStatusCode, errorCode, objectId, message); 
} 
.... 

编辑: 如果你想指导你的API的用户不仅仅是通过你的单证更好,你当然可以提供专门的工厂方法,例如

//e.g. needs no message, the HTTP status code is enough 
public ExamRestServiceException connectionError(int httpStatusCode) {…} 

public ExamRestServiceException(int errorCode, String message) { … } 

或甚至提供每个错误条件的一种方法。顺便说一下,有一个完全可用的HttpRetryException,你可能想重用,而不是自己滚动。如果(并且只有在!)你担心你的异常类变得太笨重时,你应该考虑把它分成多个类。

+0

但是,当我需要创建一个新的ExampleRestServiceException实例时,我必须提供所有的属性。例如,应该抛出一个无效的studentId异常,其errorCode为1,错误消息“ID为[115]的学生不存在”。和一个httpdStatus代码404或400,取决于studentId来自何处,将分散在整个代码库中(可能会从多个不同的操作抛出相同的异常)。这正是我想要避免的第一位。 – 2014-09-05 15:16:04

1

llogiq的另一种方法是有一个异常构建器或工厂。 (a)提供一致的例外,(b)如果开发人员需要检查代码,则将代码保存在一个地方(c)如果不需要构建单个异常知道。