Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java REST服务的异常层次结构_Java_Oop - Fatal编程技术网

Java REST服务的异常层次结构

Java REST服务的异常层次结构,java,oop,Java,Oop,我正在为REST服务设计一个异常类层次结构,并面临以下问题请注意,该解决方案必须与Java 7兼容。 假设我正在构建一个系统来管理考试。假设我拥有以下资源: 学生(学生ID、名、姓……) 课程(课程ID,名称,…) 考试(考试、成绩等) 以及以下操作(除其他外): 获取/students/{studentId} 获取/courses/{courseId} POST/students/{studentId}/考试(包含成绩和课程ID的正文) 请注意,对于某些操作,courseId作为URL

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

假设我正在构建一个系统来管理考试。假设我拥有以下资源:

  • 学生(学生ID、名、姓……)
  • 课程(课程ID,名称,…)
  • 考试(考试、成绩等)
以及以下操作(除其他外):

  • 获取/students/{studentId}
  • 获取/courses/{courseId}
  • POST/students/{studentId}/考试(包含成绩和课程ID的正文)
请注意,对于某些操作,courseId作为URL的一部分提供,对于其他操作,courseId作为请求正文的一部分提供

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

  • statusCode—响应的HTTP状态代码
  • errorCode—错误的唯一标识符
  • errorMessage—对错误的可读描述
现在让我们关注类似“无效ID”的错误。我有以下要求:

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

  • errorCode/errorMessage应该从业务角度识别错误。错误代码应该是唯一标识错误原因的数字(比如1表示无效的studentId,2表示无效的courseId等),错误消息应该以可编辑的格式描述原因(例如,“ID为[135]的学生不存在”)

问题是,如何将这两个ortogonal层次结构组合成一个异常类层次结构

理想情况下,我会有以下异常类:

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原则(我希望每个常量值只定义在一个位置)?

不要在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) {…}


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

< P>一个LLogGQ的替代方法是有一个异常生成器或工厂。
这(a)提供了一致的异常,(b)如果开发人员需要检查代码,则将代码保留在一个位置,(c)如果不需要知道个别异常是如何构造的,则使开发人员不必知道这些异常是如何构造的。

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

例外情况不应按名称传输原因。在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.");
       }

    }

稍后,当您开发应用程序时,您可能会更改异常的设计。此组合将允许您这样做。当您使用继承而不是组合时,您的代码就成了中继对象耦合,这抑制了更改和重用的潜力

我想你应该看看接吻的原则,而不是干这里。从开发人员的角度来看,我希望能够快速查找导致异常的原因,或者在抛出异常时会发生什么。我不想拖网一个复杂的层次结构来实现这一点,但是当我需要创建一个新的ExampleRestServiceException实例时,我必须提供所有的属性。错误代码为1、错误消息为“ID为[115]的学生不存在”以及httpStatus代码为404或400(取决于studentId的来源)的信息(例如无效studentId异常)将分散在整个代码库中(同一个异常可能会从多个不同的操作中抛出)这是我首先要避免的。
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.");
       }

    }