ErrorResponse support in Spring MVC exception hierarchy
All Spring MVC exceptions from spring-web, now implement ErrorResponse and expose HTTP error response information, including an RFC 7807 body. See gh-27052
This commit is contained in:
parent
3efedef161
commit
76be6373a8
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ProblemDetail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base for exceptions related to media types. Adds a list of supported {@link MediaType MediaTypes}.
|
* Abstract base for exceptions related to media types. Adds a list of supported {@link MediaType MediaTypes}.
|
||||||
|
|
@ -30,10 +31,12 @@ import org.springframework.http.MediaType;
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public abstract class HttpMediaTypeException extends ServletException {
|
public abstract class HttpMediaTypeException extends ServletException implements ErrorResponse {
|
||||||
|
|
||||||
private final List<MediaType> supportedMediaTypes;
|
private final List<MediaType> supportedMediaTypes;
|
||||||
|
|
||||||
|
private final ProblemDetail body = ProblemDetail.forRawStatusCode(getRawStatusCode());
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new HttpMediaTypeException.
|
* Create a new HttpMediaTypeException.
|
||||||
|
|
@ -61,4 +64,9 @@ public abstract class HttpMediaTypeException extends ServletException {
|
||||||
return this.supportedMediaTypes;
|
return this.supportedMediaTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProblemDetail getBody() {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2012 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -18,10 +18,14 @@ package org.springframework.web;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception thrown when the request handler cannot generate a response that is acceptable by the client.
|
* Exception thrown when the request handler cannot generate a response that is
|
||||||
|
* acceptable by the client.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
|
@ -30,11 +34,12 @@ import org.springframework.http.MediaType;
|
||||||
public class HttpMediaTypeNotAcceptableException extends HttpMediaTypeException {
|
public class HttpMediaTypeNotAcceptableException extends HttpMediaTypeException {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new HttpMediaTypeNotAcceptableException.
|
* Constructor for when the {@code Accept} header cannot be parsed.
|
||||||
* @param message the exception message
|
* @param message the parse error message
|
||||||
*/
|
*/
|
||||||
public HttpMediaTypeNotAcceptableException(String message) {
|
public HttpMediaTypeNotAcceptableException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
|
getBody().setDetail("Could not parse Accept header");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -42,7 +47,23 @@ public class HttpMediaTypeNotAcceptableException extends HttpMediaTypeException
|
||||||
* @param supportedMediaTypes the list of supported media types
|
* @param supportedMediaTypes the list of supported media types
|
||||||
*/
|
*/
|
||||||
public HttpMediaTypeNotAcceptableException(List<MediaType> supportedMediaTypes) {
|
public HttpMediaTypeNotAcceptableException(List<MediaType> supportedMediaTypes) {
|
||||||
super("Could not find acceptable representation", supportedMediaTypes);
|
super("No acceptable representation", supportedMediaTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() {
|
||||||
|
return HttpStatus.NOT_ACCEPTABLE.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
if (CollectionUtils.isEmpty(getSupportedMediaTypes())) {
|
||||||
|
return HttpHeaders.EMPTY;
|
||||||
|
}
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setAccept(this.getSupportedMediaTypes());
|
||||||
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -18,14 +18,19 @@ package org.springframework.web;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception thrown when a client POSTs, PUTs, or PATCHes content of a type
|
* Exception thrown when a client POSTs, PUTs, or PATCHes content of a type
|
||||||
* not supported by request handler.
|
* not supported by request handler.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
|
|
@ -34,6 +39,9 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException {
|
||||||
@Nullable
|
@Nullable
|
||||||
private final MediaType contentType;
|
private final MediaType contentType;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final HttpMethod httpMethod;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new HttpMediaTypeNotSupportedException.
|
* Create a new HttpMediaTypeNotSupportedException.
|
||||||
|
|
@ -42,6 +50,8 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException {
|
||||||
public HttpMediaTypeNotSupportedException(String message) {
|
public HttpMediaTypeNotSupportedException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.contentType = null;
|
this.contentType = null;
|
||||||
|
this.httpMethod = null;
|
||||||
|
getBody().setDetail("Could not parse Content-Type");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -50,21 +60,38 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException {
|
||||||
* @param supportedMediaTypes the list of supported media types
|
* @param supportedMediaTypes the list of supported media types
|
||||||
*/
|
*/
|
||||||
public HttpMediaTypeNotSupportedException(@Nullable MediaType contentType, List<MediaType> supportedMediaTypes) {
|
public HttpMediaTypeNotSupportedException(@Nullable MediaType contentType, List<MediaType> supportedMediaTypes) {
|
||||||
this(contentType, supportedMediaTypes, "Content type '" +
|
this(contentType, supportedMediaTypes, null);
|
||||||
(contentType != null ? contentType : "") + "' not supported");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new HttpMediaTypeNotSupportedException.
|
* Create a new HttpMediaTypeNotSupportedException.
|
||||||
* @param contentType the unsupported content type
|
* @param contentType the unsupported content type
|
||||||
* @param supportedMediaTypes the list of supported media types
|
* @param supportedMediaTypes the list of supported media types
|
||||||
* @param msg the detail message
|
* @param httpMethod the HTTP method of the request
|
||||||
|
* @since 6.0
|
||||||
*/
|
*/
|
||||||
public HttpMediaTypeNotSupportedException(@Nullable MediaType contentType,
|
public HttpMediaTypeNotSupportedException(@Nullable MediaType contentType,
|
||||||
List<MediaType> supportedMediaTypes, String msg) {
|
List<MediaType> supportedMediaTypes, @Nullable HttpMethod httpMethod) {
|
||||||
|
|
||||||
super(msg, supportedMediaTypes);
|
this(contentType, supportedMediaTypes, httpMethod,
|
||||||
|
"Content-Type " + (contentType != null ? "'" + contentType + "' " : "") + "is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new HttpMediaTypeNotSupportedException.
|
||||||
|
* @param contentType the unsupported content type
|
||||||
|
* @param supportedMediaTypes the list of supported media types
|
||||||
|
* @param httpMethod the HTTP method of the request
|
||||||
|
* @param message the detail message
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
public HttpMediaTypeNotSupportedException(@Nullable MediaType contentType,
|
||||||
|
List<MediaType> supportedMediaTypes, @Nullable HttpMethod httpMethod, String message) {
|
||||||
|
|
||||||
|
super(message, supportedMediaTypes);
|
||||||
this.contentType = contentType;
|
this.contentType = contentType;
|
||||||
|
this.httpMethod = httpMethod;
|
||||||
|
getBody().setDetail("Content-Type " + this.contentType + " is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -76,4 +103,22 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException {
|
||||||
return this.contentType;
|
return this.contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() {
|
||||||
|
return HttpStatus.UNSUPPORTED_MEDIA_TYPE.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
if (CollectionUtils.isEmpty(getSupportedMediaTypes())) {
|
||||||
|
return HttpHeaders.EMPTY;
|
||||||
|
}
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setAccept(getSupportedMediaTypes());
|
||||||
|
if (HttpMethod.PATCH.equals(this.httpMethod)) {
|
||||||
|
headers.setAcceptPatch(getSupportedMediaTypes());
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -22,8 +22,12 @@ import java.util.Set;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ProblemDetail;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -34,13 +38,15 @@ import org.springframework.util.StringUtils;
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class HttpRequestMethodNotSupportedException extends ServletException {
|
public class HttpRequestMethodNotSupportedException extends ServletException implements ErrorResponse {
|
||||||
|
|
||||||
private final String method;
|
private final String method;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String[] supportedMethods;
|
private final String[] supportedMethods;
|
||||||
|
|
||||||
|
private final ProblemDetail body;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new HttpRequestMethodNotSupportedException.
|
* Create a new HttpRequestMethodNotSupportedException.
|
||||||
|
|
@ -74,7 +80,7 @@ public class HttpRequestMethodNotSupportedException extends ServletException {
|
||||||
* @param supportedMethods the actually supported HTTP methods (may be {@code null})
|
* @param supportedMethods the actually supported HTTP methods (may be {@code null})
|
||||||
*/
|
*/
|
||||||
public HttpRequestMethodNotSupportedException(String method, @Nullable String[] supportedMethods) {
|
public HttpRequestMethodNotSupportedException(String method, @Nullable String[] supportedMethods) {
|
||||||
this(method, supportedMethods, "Request method '" + method + "' not supported");
|
this(method, supportedMethods, "Request method '" + method + "' is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -87,6 +93,8 @@ public class HttpRequestMethodNotSupportedException extends ServletException {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.supportedMethods = supportedMethods;
|
this.supportedMethods = supportedMethods;
|
||||||
|
this.body = ProblemDetail.forRawStatusCode(getRawStatusCode())
|
||||||
|
.withDetail("Method '" + method + "' is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -123,4 +131,24 @@ public class HttpRequestMethodNotSupportedException extends ServletException {
|
||||||
return supportedMethods;
|
return supportedMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() {
|
||||||
|
return HttpStatus.METHOD_NOT_ALLOWED.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
if (ObjectUtils.isEmpty(this.supportedMethods)) {
|
||||||
|
return HttpHeaders.EMPTY;
|
||||||
|
}
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.add(HttpHeaders.ALLOW, StringUtils.arrayToDelimitedString(this.supportedMethods, ", "));
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProblemDetail getBody() {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,9 +17,12 @@
|
||||||
package org.springframework.web.bind;
|
package org.springframework.web.bind;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ProblemDetail;
|
||||||
import org.springframework.validation.BindException;
|
import org.springframework.validation.BindException;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.validation.ObjectError;
|
import org.springframework.validation.ObjectError;
|
||||||
|
import org.springframework.web.ErrorResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception to be thrown when validation on an argument annotated with {@code @Valid} fails.
|
* Exception to be thrown when validation on an argument annotated with {@code @Valid} fails.
|
||||||
|
|
@ -30,10 +33,12 @@ import org.springframework.validation.ObjectError;
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class MethodArgumentNotValidException extends BindException {
|
public class MethodArgumentNotValidException extends BindException implements ErrorResponse {
|
||||||
|
|
||||||
private final MethodParameter parameter;
|
private final MethodParameter parameter;
|
||||||
|
|
||||||
|
private final ProblemDetail body;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for {@link MethodArgumentNotValidException}.
|
* Constructor for {@link MethodArgumentNotValidException}.
|
||||||
|
|
@ -43,9 +48,20 @@ public class MethodArgumentNotValidException extends BindException {
|
||||||
public MethodArgumentNotValidException(MethodParameter parameter, BindingResult bindingResult) {
|
public MethodArgumentNotValidException(MethodParameter parameter, BindingResult bindingResult) {
|
||||||
super(bindingResult);
|
super(bindingResult);
|
||||||
this.parameter = parameter;
|
this.parameter = parameter;
|
||||||
|
this.body = ProblemDetail.forRawStatusCode(getRawStatusCode()).withDetail(initMessage(parameter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() {
|
||||||
|
return HttpStatus.BAD_REQUEST.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProblemDetail getBody() {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the method parameter that failed validation.
|
* Return the method parameter that failed validation.
|
||||||
*/
|
*/
|
||||||
|
|
@ -55,9 +71,13 @@ public class MethodArgumentNotValidException extends BindException {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
|
return initMessage(this.parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String initMessage(MethodParameter parameter) {
|
||||||
StringBuilder sb = new StringBuilder("Validation failed for argument [")
|
StringBuilder sb = new StringBuilder("Validation failed for argument [")
|
||||||
.append(this.parameter.getParameterIndex()).append("] in ")
|
.append(parameter.getParameterIndex()).append("] in ")
|
||||||
.append(this.parameter.getExecutable().toGenericString());
|
.append(parameter.getExecutable().toGenericString());
|
||||||
BindingResult bindingResult = getBindingResult();
|
BindingResult bindingResult = getBindingResult();
|
||||||
if (bindingResult.getErrorCount() > 1) {
|
if (bindingResult.getErrorCount() > 1) {
|
||||||
sb.append(" with ").append(bindingResult.getErrorCount()).append(" errors");
|
sb.append(" with ").append(bindingResult.getErrorCount()).append(" errors");
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -57,6 +57,7 @@ public class MissingMatrixVariableException extends MissingRequestValueException
|
||||||
super("", missingAfterConversion);
|
super("", missingAfterConversion);
|
||||||
this.variableName = variableName;
|
this.variableName = variableName;
|
||||||
this.parameter = parameter;
|
this.parameter = parameter;
|
||||||
|
getBody().setDetail("Required path parameter '" + this.variableName + "' is not present");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.web.bind;
|
package org.springframework.web.bind;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ServletRequestBindingException} subclass that indicates that a path
|
* {@link ServletRequestBindingException} subclass that indicates that a path
|
||||||
|
|
@ -59,6 +60,7 @@ public class MissingPathVariableException extends MissingRequestValueException {
|
||||||
super("", missingAfterConversion);
|
super("", missingAfterConversion);
|
||||||
this.variableName = variableName;
|
this.variableName = variableName;
|
||||||
this.parameter = parameter;
|
this.parameter = parameter;
|
||||||
|
getBody().setDetail("Required URI variable '" + this.variableName + "' is not present");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -83,4 +85,10 @@ public class MissingPathVariableException extends MissingRequestValueException {
|
||||||
return this.parameter;
|
return this.parameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() {
|
||||||
|
return HttpStatus.INTERNAL_SERVER_ERROR.value();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -57,6 +57,7 @@ public class MissingRequestCookieException extends MissingRequestValueException
|
||||||
super("", missingAfterConversion);
|
super("", missingAfterConversion);
|
||||||
this.cookieName = cookieName;
|
this.cookieName = cookieName;
|
||||||
this.parameter = parameter;
|
this.parameter = parameter;
|
||||||
|
getBody().setDetail("Required cookie '" + this.cookieName + "' is not present");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -57,6 +57,7 @@ public class MissingRequestHeaderException extends MissingRequestValueException
|
||||||
super("", missingAfterConversion);
|
super("", missingAfterConversion);
|
||||||
this.headerName = headerName;
|
this.headerName = headerName;
|
||||||
this.parameter = parameter;
|
this.parameter = parameter;
|
||||||
|
getBody().setDetail("Required header '" + this.headerName + "' is not present");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -52,6 +52,7 @@ public class MissingServletRequestParameterException extends MissingRequestValue
|
||||||
super("", missingAfterConversion);
|
super("", missingAfterConversion);
|
||||||
this.parameterName = parameterName;
|
this.parameterName = parameterName;
|
||||||
this.parameterType = parameterType;
|
this.parameterType = parameterType;
|
||||||
|
getBody().setDetail("Required parameter '" + this.parameterName + "' is not present");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2012 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
package org.springframework.web.bind;
|
package org.springframework.web.bind;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ProblemDetail;
|
||||||
|
import org.springframework.web.ErrorResponse;
|
||||||
import org.springframework.web.util.NestedServletException;
|
import org.springframework.web.util.NestedServletException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -30,7 +33,10 @@ import org.springframework.web.util.NestedServletException;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class ServletRequestBindingException extends NestedServletException {
|
public class ServletRequestBindingException extends NestedServletException implements ErrorResponse {
|
||||||
|
|
||||||
|
private final ProblemDetail body = ProblemDetail.forRawStatusCode(getRawStatusCode());
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for ServletRequestBindingException.
|
* Constructor for ServletRequestBindingException.
|
||||||
|
|
@ -49,4 +55,15 @@ public class ServletRequestBindingException extends NestedServletException {
|
||||||
super(msg, cause);
|
super(msg, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() {
|
||||||
|
return HttpStatus.BAD_REQUEST.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProblemDetail getBody() {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,6 +16,10 @@
|
||||||
|
|
||||||
package org.springframework.web.context.request.async;
|
package org.springframework.web.context.request.async;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ProblemDetail;
|
||||||
|
import org.springframework.web.ErrorResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception to be thrown when an async request times out.
|
* Exception to be thrown when an async request times out.
|
||||||
* Alternatively an applications can register a
|
* Alternatively an applications can register a
|
||||||
|
|
@ -30,6 +34,16 @@ package org.springframework.web.context.request.async;
|
||||||
* @since 4.2.8
|
* @since 4.2.8
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class AsyncRequestTimeoutException extends RuntimeException {
|
public class AsyncRequestTimeoutException extends RuntimeException implements ErrorResponse {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() {
|
||||||
|
return HttpStatus.SERVICE_UNAVAILABLE.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProblemDetail getBody() {
|
||||||
|
return ProblemDetail.forRawStatusCode(getRawStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ public class MissingServletRequestPartException extends ServletRequestBindingExc
|
||||||
public MissingServletRequestPartException(String requestPartName) {
|
public MissingServletRequestPartException(String requestPartName) {
|
||||||
super("Required request part '" + requestPartName + "' is not present");
|
super("Required request part '" + requestPartName + "' is not present");
|
||||||
this.requestPartName = requestPartName;
|
this.requestPartName = requestPartName;
|
||||||
|
getBody().setDetail(getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -156,7 +156,7 @@ public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, A
|
||||||
|
|
||||||
private <R> Mono<R> createNotFoundError() {
|
private <R> Mono<R> createNotFoundError() {
|
||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
Exception ex = new ResponseStatusException(HttpStatus.NOT_FOUND, "No matching handler");
|
Exception ex = new ResponseStatusException(HttpStatus.NOT_FOUND);
|
||||||
return Mono.error(ex);
|
return Mono.error(ex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -1261,8 +1261,7 @@ public abstract class RouterFunctions {
|
||||||
}
|
}
|
||||||
|
|
||||||
private <R> Mono<R> createNotFoundError() {
|
private <R> Mono<R> createNotFoundError() {
|
||||||
return Mono.defer(() -> Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND,
|
return Mono.defer(() -> Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND)));
|
||||||
"No matching router function")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> Mono<T> wrapException(Supplier<Mono<T>> supplier) {
|
private static <T> Mono<T> wrapException(Supplier<Mono<T>> supplier) {
|
||||||
|
|
|
||||||
|
|
@ -200,8 +200,8 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||||
|
|
||||||
if (helper.hasParamsMismatch()) {
|
if (helper.hasParamsMismatch()) {
|
||||||
throw new ServerWebInputException(
|
throw new ServerWebInputException(
|
||||||
"Unsatisfied query parameter conditions: " + helper.getParamConditions() +
|
"Expected parameters: " + helper.getParamConditions() +
|
||||||
", actual parameters: " + request.getQueryParams());
|
", actual query parameters: " + request.getQueryParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -84,7 +84,7 @@ public class DispatcherHandlerErrorTests {
|
||||||
StepVerifier.create(mono)
|
StepVerifier.create(mono)
|
||||||
.consumeErrorWith(ex -> {
|
.consumeErrorWith(ex -> {
|
||||||
assertThat(ex).isInstanceOf(ResponseStatusException.class);
|
assertThat(ex).isInstanceOf(ResponseStatusException.class);
|
||||||
assertThat(ex.getMessage()).isEqualTo("404 NOT_FOUND \"No matching handler\"");
|
assertThat(ex.getMessage()).isEqualTo("404 NOT_FOUND");
|
||||||
})
|
})
|
||||||
.verify();
|
.verify();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -21,10 +21,13 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ProblemDetail;
|
||||||
|
import org.springframework.web.ErrorResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By default when the DispatcherServlet can't find a handler for a request it
|
* By default, when the DispatcherServlet can't find a handler for a request it
|
||||||
* sends a 404 response. However if its property "throwExceptionIfNoHandlerFound"
|
* sends a 404 response. However, if its property "throwExceptionIfNoHandlerFound"
|
||||||
* is set to {@code true} this exception is raised and may be handled with
|
* is set to {@code true} this exception is raised and may be handled with
|
||||||
* a configured HandlerExceptionResolver.
|
* a configured HandlerExceptionResolver.
|
||||||
*
|
*
|
||||||
|
|
@ -34,7 +37,7 @@ import org.springframework.http.HttpHeaders;
|
||||||
* @see DispatcherServlet#noHandlerFound(HttpServletRequest, HttpServletResponse)
|
* @see DispatcherServlet#noHandlerFound(HttpServletRequest, HttpServletResponse)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class NoHandlerFoundException extends ServletException {
|
public class NoHandlerFoundException extends ServletException implements ErrorResponse {
|
||||||
|
|
||||||
private final String httpMethod;
|
private final String httpMethod;
|
||||||
|
|
||||||
|
|
@ -42,6 +45,8 @@ public class NoHandlerFoundException extends ServletException {
|
||||||
|
|
||||||
private final HttpHeaders headers;
|
private final HttpHeaders headers;
|
||||||
|
|
||||||
|
private final ProblemDetail detail = ProblemDetail.forRawStatusCode(getRawStatusCode());
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for NoHandlerFoundException.
|
* Constructor for NoHandlerFoundException.
|
||||||
|
|
@ -57,6 +62,11 @@ public class NoHandlerFoundException extends ServletException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() {
|
||||||
|
return HttpStatus.NOT_FOUND.value();
|
||||||
|
}
|
||||||
|
|
||||||
public String getHttpMethod() {
|
public String getHttpMethod() {
|
||||||
return this.httpMethod;
|
return this.httpMethod;
|
||||||
}
|
}
|
||||||
|
|
@ -69,4 +79,9 @@ public class NoHandlerFoundException extends ServletException {
|
||||||
return this.headers;
|
return this.headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProblemDetail getBody() {
|
||||||
|
return this.detail;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -206,7 +206,7 @@ class DefaultServerRequest implements ServerRequest {
|
||||||
return theConverter.read(clazz, this.serverHttpRequest);
|
return theConverter.read(clazz, this.serverHttpRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new HttpMediaTypeNotSupportedException(contentType, getSupportedMediaTypes(bodyClass));
|
throw new HttpMediaTypeNotSupportedException(contentType, getSupportedMediaTypes(bodyClass), method());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MediaType> getSupportedMediaTypes(Class<?> bodyClass) {
|
private List<MediaType> getSupportedMediaTypes(Class<?> bodyClass) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -313,7 +313,7 @@ class DefaultServerRequestBuilder implements ServerRequest.Builder {
|
||||||
return theConverter.read(clazz, inputMessage);
|
return theConverter.read(clazz, inputMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new HttpMediaTypeNotSupportedException(contentType, Collections.emptyList());
|
throw new HttpMediaTypeNotSupportedException(contentType, Collections.emptyList(), method());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -264,7 +264,8 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||||
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
|
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
|
throw new HttpMediaTypeNotSupportedException(
|
||||||
|
contentType, new ArrayList<>(mediaTypes), HttpMethod.valueOf(request.getMethod()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (helper.hasProducesMismatch()) {
|
if (helper.hasProducesMismatch()) {
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
throw new HttpMediaTypeNotSupportedException(contentType,
|
throw new HttpMediaTypeNotSupportedException(contentType,
|
||||||
getSupportedMediaTypes(targetClass != null ? targetClass : Object.class));
|
getSupportedMediaTypes(targetClass != null ? targetClass : Object.class), httpMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaType selectedContentType = contentType;
|
MediaType selectedContentType = contentType;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue