refactor: optimize the exception information (#3747)

#### What type of PR is this?
/kind improvement
/area core

#### What this PR does / why we need it:
优化异常信息
- 5xx 服务器内部错误不显示异常详情到页面,如主题模板表达式错误
- 访问 `GET /apis/api.halo.run/v1alpha1/comments` 提示 400 且不会打印异常堆栈
- 访问不存在的主题静态资源提示 404 且不会打印异常堆栈,如 `GET /themes/guqing-higan/assets/dist/style1.css`

#### Which issue(s) this PR fixes:
Fixes #3483

#### Does this PR introduce a user-facing change?
```release-note
None
```
pull/3792/head
guqing 2023-04-19 18:26:24 +08:00 committed by GitHub
parent a94c0c7f85
commit 5477e30781
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 16 additions and 6 deletions

View File

@ -8,6 +8,7 @@ import java.time.Instant;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes; import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorAttributes; import org.springframework.boot.web.reactive.error.ErrorAttributes;
@ -39,7 +40,7 @@ public class ProblemDetailErrorAttributes implements ErrorAttributes {
@Override @Override
public Map<String, Object> getErrorAttributes(ServerRequest request, public Map<String, Object> getErrorAttributes(ServerRequest request,
ErrorAttributeOptions options) { ErrorAttributeOptions options) {
var errAttributes = new LinkedHashMap<String, Object>(); final var errAttributes = new LinkedHashMap<String, Object>();
var error = getError(request); var error = getError(request);
var responseStatusAnno = from(error.getClass(), SearchStrategy.TYPE_HIERARCHY) var responseStatusAnno = from(error.getClass(), SearchStrategy.TYPE_HIERARCHY)
@ -50,8 +51,12 @@ public class ProblemDetailErrorAttributes implements ErrorAttributes {
if (error instanceof ErrorResponse er) { if (error instanceof ErrorResponse er) {
errorResponse = er; errorResponse = er;
} else { } else {
var reason = responseStatusAnno.getValue("reason", String.class) var reason = Optional.of(status)
.orElse(error.getMessage()); .filter(HttpStatusCode::is5xxServerError)
.map(s -> "Something went wrong, please try again later.")
.orElseGet(() -> responseStatusAnno.getValue("reason", String.class)
.orElse(error.getMessage())
);
errorResponse = ErrorResponse.create(error, status, reason); errorResponse = ErrorResponse.create(error, status, reason);
} }
var problemDetail = var problemDetail =

View File

@ -46,6 +46,9 @@ public class ThemeConfiguration {
var resource = request.pathVariable("resource"); var resource = request.pathVariable("resource");
resource = StringUtils.removeStart(resource, "/"); resource = StringUtils.removeStart(resource, "/");
var fsRes = new FileSystemResource(getThemeAssetsPath(themeName, resource)); var fsRes = new FileSystemResource(getThemeAssetsPath(themeName, resource));
if (!fsRes.exists()) {
return ServerResponse.notFound().build();
}
var bodyBuilder = ServerResponse.ok() var bodyBuilder = ServerResponse.ok()
.cacheControl(cacheProperties.getCachecontrol().toHttpCacheControl()); .cacheControl(cacheProperties.getCachecontrol().toHttpCacheControl());
try { try {

View File

@ -22,6 +22,7 @@ import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebInputException;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers; import reactor.core.scheduler.Schedulers;
import run.halo.app.content.comment.CommentRequest; import run.halo.app.content.comment.CommentRequest;
@ -230,7 +231,7 @@ public class CommentFinderEndpoint implements CustomEndpoint {
public String getKind() { public String getKind() {
String kind = emptyToNull(queryParams.getFirst("kind")); String kind = emptyToNull(queryParams.getFirst("kind"));
if (kind == null) { if (kind == null) {
throw new IllegalArgumentException("The kind must not be null."); throw new ServerWebInputException("The kind must not be null.");
} }
return kind; return kind;
} }
@ -244,7 +245,7 @@ public class CommentFinderEndpoint implements CustomEndpoint {
public String getName() { public String getName() {
String name = emptyToNull(queryParams.getFirst("name")); String name = emptyToNull(queryParams.getFirst("name"));
if (name == null) { if (name == null) {
throw new IllegalArgumentException("The name must not be null."); throw new ServerWebInputException("The name must not be null.");
} }
return name; return name;
} }

View File

@ -105,13 +105,14 @@ class I18nExceptionTest {
@Test @Test
void shouldGetErrorIfThrowingGeneralException() { void shouldGetErrorIfThrowingGeneralException() {
// problem reason will be a fixed prompt when internal server error occurred.
webClient.get().uri("/response-entity/general-error") webClient.get().uri("/response-entity/general-error")
.exchange() .exchange()
.expectStatus().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR) .expectStatus().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR)
.expectBody(ProblemDetail.class) .expectBody(ProblemDetail.class)
.value(problemDetail -> { .value(problemDetail -> {
assertEquals("Internal Server Error", problemDetail.getTitle()); assertEquals("Internal Server Error", problemDetail.getTitle());
assertEquals("Something went wrong", assertEquals("Something went wrong, please try again later.",
problemDetail.getDetail()); problemDetail.getDetail());
}); });
} }