java 在Quarkus/RESTEasy中,如何为格式错误的查询参数显示有用的错误消息?

fnatzsnv  于 2023-06-20  发布在  Java
关注(0)|答案(4)|浏览(123)

我有一个REST端点,看起来像这样:

@Path("/api/v1/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UsersResource {
  @GET
  public Uni<ListUsersResponse> list(@BeanParam @Valid ListUsersRequest req) {
    ...
  }
}
  
...

public class ListUsersRequest {
  @NotNull
  @NotEmpty
  @QueryParam("status_in")
  List<UserStatus> statusIn;
}

...

public enum UserStatus {
  All,
  Creating,
  Deleting,
  ...
}

当我像这样调用一个API时:
http://localhost:8080/api/v1/users?status_in=invalid_status
Quarkus/RESTEasy将返回404,服务器日志中甚至不会显示任何内容,因为它是404,因为它是“客户端错误”。
如果我启用调试日志记录,错误如下所示:

java.lang.IllegalArgumentException: No enum constant com.myproject.UserStatus.something

我不喜欢这种行为,因为:

  1. 404意味着“未找到”,这会让用户认为他们使用的路径根本不存在,这是令人困惑的。
    1.用户和开发人员都很难找出问题所在,因为在没有启用DEBUG的情况下,响应或服务器日志中没有错误消息。
    显然,这是故意的,因为它符合some spec
    3.2字段和Bean属性
    如果字段或属性使用@MatrixParam、@QueryParam或@PathParam注解,则实现必须生成一个NotFoundException(404 status)示例,该示例 Package 抛出的异常,而不包含实体
    所以我的问题是,我如何修改它来实际给予有用的错误消息和状态代码?
68de4m5k

68de4m5k1#

正是出于这个原因,我创建了各种异常处理程序。例如,我想检查传入的JSON主体是否格式错误。为此我做了:

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.jboss.resteasy.spi.ReaderException;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

/**
 * Maps a ReaderException to a response.  A ReaderException is most often thrown
 * when we get a bad JSON packet.
 *
 */
@Provider
public class ReaderExceptionMapper implements ExceptionMapper<ReaderException> {

    @Override
    public Response toResponse(ReaderException exception) {

        if( exception.getCause() instanceof JsonParseException ||
                exception.getCause() instanceof JsonMappingException ) {
            return Response.status(Response.Status.BAD_REQUEST)
                    .entity("error parsing json body - " + exception.getMessage())
                    .type(MediaType.TEXT_PLAIN_TYPE)
                    .build();
        }

        return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(exception.getMessage())
                .type(MediaType.TEXT_PLAIN_TYPE)
                .build();
    }
}

在您的例子中,您应该能够为IllegalArgumentException创建一个ExceptionMapper并执行您想要的操作。我在Wildfly和Quarkus中都运行了这个。

ghhaqwfi

ghhaqwfi2#

您可以使用ParamConverter作为通用枚举类型。

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;

@Provider
public class EnumParamConverterProvider implements ParamConverterProvider {
    @Override
    public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType, final Annotation[] annotations) {
        if (!rawType.isEnum()) {
            return null;
        }
        final Enum<?>[] constants = (Enum<?>[]) rawType.getEnumConstants();
        return new ParamConverter<T>() {
            @Override
            @SuppressWarnings("unchecked")
            public T fromString(final String value) {
                if (value == null || value.isEmpty()) {
                    return null;
                }
                for (Enum<?> e : constants) {
                    if (e.name().equalsIgnoreCase(value)) {
                        return (T) e;
                    }
                }
                // No match, check toString()
                for (Enum<?> e : constants) {
                    if (e.toString().equalsIgnoreCase(value)) {
                        return (T) e;
                    }
                }
                throw new WebApplicationException(Response.serverError().entity(String.format("Failed to find constant '%s'", value)).build());
            }

            @Override
            public String toString(final T value) {
                return value != null ? value.toString() : null;
            }
        };
    }
}
7qhs6swi

7qhs6swi3#

有一个类似的问题,我给了一个答案。基本上,我建议使用ResponseEntity类作为返回值,或者创建标记为@ControllerAdvice的异常处理程序。请看这里的答案:如何将消息添加到异常响应

ffvjumwh

ffvjumwh4#

这是由QueryParamInjector引起的。如果在Map查询参数时,它捕获了任何不是从javax.ws.rs.WebApplicationException继承的异常,它将把异常 Package 到NotFoundException中。(参见StringParameterInjector#extractValue(String)QueryParamInjector#throwProcessingException(String, Throwable)
您可以通过抛出扩展javax.ws.rs.WebApplicationException而不是IllegalArgumentException的异常来绕过此行为。在我的例子中,我使用自定义的ParamConverter,我将caught RuntimeException s Package 成javax.ws.rs.BadRequestException
另外,BadRequestException的自定义ExceptionMapper也会很有用,因为默认情况下会将stacktrace打印到响应中

相关问题