Skip to content

Commit 0e1fb52

Browse files
committed
#786 - Implement vendor neutral error handling with RFC-7807.
Supercedes: #775, #718, #651, #546, #478, #345
1 parent 9d83787 commit 0e1fb52

File tree

15 files changed

+490
-0
lines changed

15 files changed

+490
-0
lines changed

src/main/java/org/springframework/hateoas/MediaTypes.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,14 @@ public class MediaTypes {
7676
* Public constant media type for {@code application/vnd.amundsen-uber+json}.
7777
*/
7878
public static final MediaType UBER_JSON = MediaType.parseMediaType(UBER_JSON_VALUE);
79+
80+
/**
81+
* A String equivalent of {@link MediaTypes#PROBLEM_JSON_VALUE}.
82+
*/
83+
public static final String PROBLEM_JSON_VALUE = "application/problem+json";
84+
85+
/**
86+
* Public constant media type for {@code application/problem+json}.
87+
*/
88+
public static final MediaType PROBLEM_JSON = MediaType.parseMediaType(PROBLEM_JSON_VALUE);
7989
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.hateoas.mediatype.problem;
17+
18+
import java.net.URI;
19+
import java.util.Objects;
20+
21+
import org.springframework.http.HttpStatus;
22+
23+
import com.fasterxml.jackson.annotation.JsonInclude;
24+
import com.fasterxml.jackson.annotation.JsonInclude.Include;
25+
26+
/**
27+
* Encapsulation of an RFC-7807 {@literal Problem} code. While it complies out-of-the-box, it may also be extended to
28+
* support domain-specific details.
29+
*
30+
* @author Greg Turnquist
31+
*/
32+
public class Problem<T extends Problem<? extends T>> {
33+
34+
private URI type;
35+
private String title;
36+
private HttpStatus status;
37+
private String detail;
38+
private URI instance;
39+
40+
public Problem() {
41+
this(null, null, null, null, null);
42+
}
43+
44+
public Problem(URI type, String title, HttpStatus status, String detail, URI instance) {
45+
46+
this.type = type;
47+
this.title = title;
48+
this.status = status;
49+
this.detail = detail;
50+
this.instance = instance;
51+
}
52+
53+
/**
54+
* A {@link Problem} that reflects an {@link HttpStatus} code.
55+
*
56+
* @see https://tools.ietf.org/html/rfc7807#section-4.2
57+
*/
58+
public Problem(HttpStatus httpStatus) {
59+
this(URI.create("about:blank"), httpStatus.getReasonPhrase(), httpStatus, null, null);
60+
}
61+
62+
63+
@SuppressWarnings("unchecked")
64+
public T withType(URI type) {
65+
this.type = type;
66+
return (T) this;
67+
}
68+
69+
@SuppressWarnings("unchecked")
70+
public T withTitle(String title) {
71+
this.title = title;
72+
return (T) this;
73+
}
74+
75+
@SuppressWarnings("unchecked")
76+
public T withStatus(HttpStatus status) {
77+
this.status = status;
78+
return (T) this;
79+
}
80+
81+
@SuppressWarnings("unchecked")
82+
public T withDetail(String detail) {
83+
this.detail = detail;
84+
return (T) this;
85+
}
86+
87+
@SuppressWarnings("unchecked")
88+
public T withInstance(URI instance) {
89+
this.instance = instance;
90+
return (T) this;
91+
}
92+
93+
@JsonInclude(Include.NON_NULL)
94+
public URI getType() {
95+
return this.type;
96+
}
97+
98+
@JsonInclude(Include.NON_NULL)
99+
public String getTitle() {
100+
return this.title;
101+
}
102+
103+
@JsonInclude(Include.NON_NULL)
104+
public Integer getStatus() {
105+
if (status != null) {
106+
return status.value();
107+
}
108+
109+
return null;
110+
}
111+
112+
@JsonInclude(Include.NON_NULL)
113+
public String getDetail() {
114+
return detail;
115+
}
116+
117+
@JsonInclude(Include.NON_NULL)
118+
public URI getInstance() {
119+
return instance;
120+
}
121+
122+
@Override
123+
public boolean equals(Object o) {
124+
125+
if (this == o)
126+
return true;
127+
if (o == null || getClass() != o.getClass())
128+
return false;
129+
Problem problem = (Problem) o;
130+
return Objects.equals(type, problem.type) && //
131+
Objects.equals(title, problem.title) && //
132+
status == problem.status && //
133+
Objects.equals(detail, problem.detail) && //
134+
Objects.equals(instance, problem.instance); //
135+
}
136+
137+
@Override
138+
public int hashCode() {
139+
return Objects.hash(type, title, status, detail, instance);
140+
}
141+
142+
@Override
143+
public String toString() {
144+
145+
return "Problem{" + //
146+
"type=" + type + //
147+
", title='" + title + '\'' + //
148+
", status=" + status + //
149+
", detail='" + detail + '\'' + //
150+
", instance=" + instance + //
151+
'}';
152+
}
153+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**
2+
* Value objects to build Problem representations.
3+
*/
4+
@org.springframework.lang.NonNullApi
5+
package org.springframework.hateoas.mediatype.problem;

0 commit comments

Comments
 (0)