Skip to content

Commit 4e90e34

Browse files
Support Problem Details for HTTP APIs RFC7807 (#3552)
* HttpProblem abstraction * ProblemDetailsErrorHandler * new media types for problem details * tests * resolve comments * resolve conflicts resolve conflicts
1 parent ebcd5e1 commit 4e90e34

24 files changed

Lines changed: 1258 additions & 6 deletions

jooby/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,18 @@
6969
<scope>test</scope>
7070
</dependency>
7171

72+
<dependency>
73+
<groupId>org.junit.jupiter</groupId>
74+
<artifactId>junit-jupiter-params</artifactId>
75+
<scope>test</scope>
76+
</dependency>
77+
78+
<dependency>
79+
<groupId>org.assertj</groupId>
80+
<artifactId>assertj-core</artifactId>
81+
<scope>test</scope>
82+
</dependency>
83+
7284
<dependency>
7385
<groupId>org.jacoco</groupId>
7486
<artifactId>org.jacoco.agent</artifactId>

jooby/src/main/java/io/jooby/DefaultErrorHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public void apply(@NonNull Context ctx, @NonNull Throwable cause, @NonNull Statu
120120
}
121121
}
122122

123-
private boolean isMuted(Throwable cause, StatusCode statusCode) {
123+
protected boolean isMuted(Throwable cause, StatusCode statusCode) {
124124
return muteCodes.contains(statusCode)
125125
// same class filter
126126
|| muteTypes.stream().anyMatch(type -> type == cause.getClass())

jooby/src/main/java/io/jooby/MediaType.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,15 @@ public final class MediaType implements Comparable<MediaType> {
3636
/** APPLICATION_JSON. */
3737
public static final String JSON = "application/json";
3838

39+
/** APPLICATION_PROBLEM_JSON. */
40+
public static final String PROBLEM_JSON = "application/problem+json";
41+
3942
/** APPLICATION_XML. */
4043
public static final String XML = "application/xml";
4144

45+
/** APPLICATION_PROBLEM_XML. */
46+
public static final String PROBLEM_XML = "application/problem+xml";
47+
4248
/** TEXT_PLAIN. */
4349
public static final String TEXT = "text/plain";
4450

@@ -81,7 +87,7 @@ public final class MediaType implements Comparable<MediaType> {
8187
/** TEXT_HTML. */
8288
public static final MediaType html = new MediaType(HTML, UTF_8);
8389

84-
/** APPLICATION_JSON. */
90+
/** APPLICATION_JAVASCRIPT. */
8591
public static final MediaType js = new MediaType(JS, UTF_8);
8692

8793
/** TEXT_CSS. */

jooby/src/main/java/io/jooby/exception/InvalidCsrfToken.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.jooby.exception;
77

88
import edu.umd.cs.findbugs.annotations.Nullable;
9+
import io.jooby.problem.HttpProblem;
910

1011
/**
1112
* Generate by CSRF handler.
@@ -23,4 +24,11 @@ public class InvalidCsrfToken extends ForbiddenException {
2324
public InvalidCsrfToken(@Nullable String token) {
2425
super(token);
2526
}
27+
28+
@Override
29+
public HttpProblem toHttpProblem() {
30+
return HttpProblem.valueOf(statusCode,
31+
"Invalid CSRF token",
32+
"CSRF token '" + getMessage() + "' is invalid");
33+
}
2634
}

jooby/src/main/java/io/jooby/exception/MethodNotAllowedException.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import edu.umd.cs.findbugs.annotations.NonNull;
1111
import io.jooby.StatusCode;
12+
import io.jooby.problem.HttpProblem;
1213

1314
/**
1415
* Whether a HTTP method isn't supported. The {@link #getAllow()} contains the supported HTTP
@@ -48,4 +49,12 @@ public String getMethod() {
4849
public List<String> getAllow() {
4950
return allow;
5051
}
52+
53+
@Override
54+
public HttpProblem toHttpProblem() {
55+
return HttpProblem.valueOf(statusCode,
56+
statusCode.reason(),
57+
"HTTP method '" + getMethod() + "' is not allowed. Allowed methods are: " + allow
58+
);
59+
}
5160
}

jooby/src/main/java/io/jooby/exception/NotAcceptableException.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import edu.umd.cs.findbugs.annotations.Nullable;
99
import io.jooby.StatusCode;
10+
import io.jooby.problem.HttpProblem;
1011

1112
/**
1213
* Whether the accept header isn't acceptable.
@@ -32,4 +33,13 @@ public NotAcceptableException(@Nullable String contentType) {
3233
public @Nullable String getContentType() {
3334
return getMessage();
3435
}
36+
37+
@Override
38+
public HttpProblem toHttpProblem() {
39+
return HttpProblem.valueOf(statusCode,
40+
statusCode.reason(),
41+
"Server cannot produce a response matching the list of " +
42+
"acceptable values defined in the request's 'Accept' header"
43+
);
44+
}
3545
}

jooby/src/main/java/io/jooby/exception/NotFoundException.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import edu.umd.cs.findbugs.annotations.NonNull;
99
import io.jooby.StatusCode;
10+
import io.jooby.problem.HttpProblem;
1011

1112
/**
1213
* When a request doesn't match any of the available routes.
@@ -33,4 +34,11 @@ public NotFoundException(@NonNull String path) {
3334
public @NonNull String getRequestPath() {
3435
return getMessage();
3536
}
37+
38+
@Override
39+
public HttpProblem toHttpProblem() {
40+
return HttpProblem.valueOf(statusCode,
41+
statusCode.reason(),
42+
"Route '" + getRequestPath() + "' not found. Please verify request path");
43+
}
3644
}

jooby/src/main/java/io/jooby/exception/StatusCodeException.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,18 @@
88
import edu.umd.cs.findbugs.annotations.NonNull;
99
import edu.umd.cs.findbugs.annotations.Nullable;
1010
import io.jooby.StatusCode;
11+
import io.jooby.problem.HttpProblem;
12+
import io.jooby.problem.HttpProblemMappable;
1113

1214
/**
1315
* Runtime exception with status code.
1416
*
1517
* @author edgar
1618
* @since 2.0.0
1719
*/
18-
public class StatusCodeException extends RuntimeException {
20+
public class StatusCodeException extends RuntimeException implements HttpProblemMappable {
1921

20-
private final StatusCode statusCode;
22+
protected final StatusCode statusCode;
2123

2224
/**
2325
* Creates an error with the given status code.
@@ -59,4 +61,9 @@ public StatusCodeException(
5961
public @NonNull StatusCode getStatusCode() {
6062
return statusCode;
6163
}
64+
65+
@Override
66+
public HttpProblem toHttpProblem() {
67+
return HttpProblem.valueOf(statusCode, getMessage());
68+
}
6269
}

jooby/src/main/java/io/jooby/exception/TypeMismatchException.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.lang.reflect.Type;
99

1010
import edu.umd.cs.findbugs.annotations.NonNull;
11+
import io.jooby.problem.HttpProblem;
1112

1213
/**
1314
* Type mismatch exception. Used when a value can't be converted to the required type.
@@ -48,4 +49,10 @@ public TypeMismatchException(@NonNull String name, @NonNull Type type) {
4849
public @NonNull String getName() {
4950
return name;
5051
}
52+
53+
54+
@Override
55+
public HttpProblem toHttpProblem() {
56+
return HttpProblem.valueOf(statusCode, "Type Mismatch", getMessage());
57+
}
5158
}

jooby/src/main/java/io/jooby/exception/UnsupportedMediaType.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import edu.umd.cs.findbugs.annotations.Nullable;
99
import io.jooby.StatusCode;
10+
import io.jooby.problem.HttpProblem;
1011

1112
/**
1213
* Whether there is no decoder for the requested <code>Content-Type</code>.
@@ -32,4 +33,11 @@ public UnsupportedMediaType(@Nullable String type) {
3233
public @Nullable String getContentType() {
3334
return getMessage();
3435
}
36+
37+
@Override
38+
public HttpProblem toHttpProblem() {
39+
return HttpProblem.valueOf(statusCode,
40+
statusCode.reason(),
41+
"Media type '" + getContentType() + "' is not supported");
42+
}
3543
}

0 commit comments

Comments
 (0)