当前位置: 首页 > 知识库问答 >
问题:

Spring错误控制器响应不合格

姚阳德
2023-03-14
@RestController
public class CustomErrorController implements ErrorController
{
  private static final String PATH = "/error";

  @Override
  public String getErrorPath()
  {
     return PATH;
  }


  @RequestMapping (value = PATH)
  public ResponseEntity<WebErrorResponse> handleError(HttpStatus status, HttpServletRequest request)
  {
     WebErrorResponse response = new WebErrorResponse();

    // original requested URI
    String uri = String.valueOf(request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI));
    // status code
    String code = String.valueOf(request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE));
    // status message
    String msg = String.valueOf(request.getAttribute(RequestDispatcher.ERROR_MESSAGE));

    response.title = "Internal Server Error";
    response.type = request.getMethod() + ": " + uri;
    response.code = Integer.valueOf(code);
    response.message = msg;

    // build headers
    HttpHeaders headers = new HttpHeaders();

    headers.setContentType(MediaType.APPLICATION_JSON_UTF8);

    // build the response
    return new ResponseEntity<>(response, headers, status);
}

public class WebErrorResponse
{
/**
 * The error message.
 */
public String message;

/**
 * The status code.
 */
public int code;

/**
 * The error title.
 */
public String title;

/**
 * The error type.
 */
public String type;
}

将响应实体主体类型更改为String非常有效。怎么了?也许这是窃听器?

附注:使用Spring4.2.8,Spring靴1.3.8。

共有1个答案

南门鸿雪
2023-03-14

最终解决方案

在谷歌中经过多次尝试和错误循环和往返之后,我终于找到了一个符合我要求的解决方案。Spring中错误处理的主要问题是由默认行为和小文档引起的。

只使用Spring而不使用Spring引导是没有问题的。但同时使用这两种方法构建web(REST)服务就像地狱一样。

    null
@Configuration
public class SpringConfig extends WebMvcConfigurerAdapter
{
   // ... init stuff if needed

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
{
    // setup content negotiation (automatic detection of content types)
    configurer
            // use format parameter and extension to detect mimetype
            .favorPathExtension(true).favorParameter(true)
            // set default mimetype
            .defaultContentType(MediaType.APPLICATION_XML)
            .mediaType(...)
            // and so on ....
 }

 /**
 * Configuration of the {@link DispatcherServlet} bean.
 *
 * <p>This is needed because Spring and Spring Boot auto-configuration override each other.</p>
 *
 * @see <a href="http://stackoverflow.com/questions/28902374/spring-boot-rest-service-exception-handling">
 *      Stackoverflow - Spring Boot REST service exception handling</a>
 *
 * @param dispatcher dispatcher servlet instance
 */
@Autowired
@SuppressWarnings ("SpringJavaAutowiringInspection")
public void setupDispatcherServlet(DispatcherServlet dispatcher)
{
    // FIX: for global REST error handling
    // enable exceptions if endpoint not found (instead of static error page)
    dispatcher.setThrowExceptionIfNoHandlerFound(true);
}

/**
 * Creates the error properties used to setup the global REST error controller.
 *
 * <p>Using {@link ErrorProperties} is compliant to base implementation if Spring Boot's
 * {@link org.springframework.boot.autoconfigure.web.BasicErrorController}.</p>
 *
 *
 * @return error properties
 */
@Bean
public ErrorProperties errorProperties()
{
    ErrorProperties properties = new ErrorProperties();

    properties.setIncludeStacktrace(ErrorProperties.IncludeStacktrace.NEVER);
    properties.setPath("/error");

    return properties;
}
// ...
}
@ControllerAdvice(annotations = RestController.class)
public class WebExceptionHandler extends ResponseEntityExceptionHandler
{
/**
 * This function handles the exceptions.
 *
 * @param e the thrown exception
 *
 * @return error message as XML-document
 */
@ExceptionHandler (Exception.class)
public ResponseEntity<Object> handleErrorResponse(Exception e)
{
    logger.trace("Catching Exception in REST API.", e);

    return handleExceptionInternal(e, null, null, null, null);
}

@Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex,
                                                         Object body,
                                                         HttpHeaders headers,
                                                         HttpStatus status,
                                                         WebRequest request)
{
    logger.trace("Catching Spring Exception in REST API.");
    logger.debug("Using " + getClass().getSimpleName() + " for exception handling.");

    // fatal, should not happen
    if(ex == null) throw new NullPointerException("empty exception");

    // set defaults
    String title = "API Error";
    String msg   = ex.getMessage();

    if(status == null) status = HttpStatus.BAD_REQUEST;

    // build response body
    WebErrorResponse response = new WebErrorResponse();

    response.type = ex.getClass().getSimpleName();
    response.title = title;
    response.message = msg;
    response.code = status.value();

    // build response headers
    if(headers == null) headers = new HttpHeaders();

    try {
        headers.setContentType(getContentType(request));
    }
    catch(NullPointerException e)
    {
        // ignore (empty headers will result in default)
    }
    catch(IllegalArgumentException e)
    {
        // return only status code
        return new ResponseEntity<>(status);
    }

    return new ResponseEntity<>(response, headers, status);
}

/**
 * Checks the given request and returns the matching response content type
 * or throws an exceptions if the requested content type could not be delivered.
 *
 * @param request current request
 *
 * @return response content type matching the request
 *
 * @throws NullPointerException     if the request does not an accept header field
 * @throws IllegalArgumentException if the requested content type is not supported
 */
private static MediaType getContentType(WebRequest request) throws NullPointerException, IllegalArgumentException
{
    String accepts = request.getHeader(HttpHeaders.ACCEPT);

    if(accepts==null) throw new NullPointerException();

    // XML
    if(accepts.contains(MediaType.APPLICATION_XML_VALUE) ||
       accepts.contains(MediaType.TEXT_XML_VALUE) ||
       accepts.contains(MediaType.APPLICATION_XHTML_XML_VALUE))
        return MediaType.APPLICATION_XML;
    // JSON
    else if(accepts.contains(MediaType.APPLICATION_JSON_VALUE))
        return MediaType.APPLICATION_JSON_UTF8;
    // other
    else throw new IllegalArgumentException();
}
}
@Controller
@RequestMapping("/error")
public class CustomErrorController extends AbstractErrorController
{
    protected final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * The global settings for this error controller.
     */
    private final ErrorProperties properties;

    /**
     * Bean constructor.
     *
     * @param properties global properties
     * @param attributes default error attributes
     */
    @Autowired
    public CustomErrorController(ErrorProperties properties, ErrorAttributes attributes)
    {
        super(attributes);

        this.properties = new ErrorProperties();
    }

    @Override
    public String getErrorPath()
    {
        return this.properties.getPath();
    }

    /**
     * Returns the configuration properties of this controller.
     *
     * @return error properties
     */
    public ErrorProperties getErrorProperties()
    {
        return this.properties;
    }

    /**
     * This function handles runtime and application errors.
     *
     * @param request the incorrect request instance
     *
     * @return error message as XML-document
     */
    @RequestMapping (produces = {MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.APPLICATION_XML_VALUE})
    @ResponseBody
    public ResponseEntity<Object> handleError(HttpServletRequest request)
    {
        logger.trace("Catching Exception in REST API.");
        logger.debug("Using {} for exception handling." , getClass().getSimpleName());

        // original requested REST endpoint
        String endpoint = String.valueOf(request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI));
        // status code
        String code = String.valueOf(request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE));
        // thrown exception
        Exception ex = ((Exception) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION));

        if(ex == null) {
            ex = new RuntimeException(String.valueOf(request.getAttribute(RequestDispatcher.ERROR_MESSAGE)));
        }

        // release nested exceptions (we want source exception only)
        if(ex instanceof NestedServletException && ex.getCause() instanceof Exception) {
            ex = (Exception) ex.getCause();
        }

        // build response body
        WebErrorResponse response = new WebErrorResponse();

        response.title   = "Internal Server Error";
        response.type    = ex.getClass().getSimpleName();
        response.code    = Integer.valueOf(code);
        response.message = request.getMethod() + ": " + endpoint+"; "+ex.getMessage();

        // build response headers
        HttpHeaders headers = new HttpHeaders();

        headers.setContentType(getResponseType(request));

        // build the response
        return new ResponseEntity<>(response, headers, getStatus(request));
    }

    /*@RequestMapping (produces = {MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.APPLICATION_XML_VALUE})
    public ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request)
    {
        Boolean stacktrace = properties.getIncludeStacktrace().equals(ErrorProperties.IncludeStacktrace.ALWAYS);

        Map<String, Object> r = getErrorAttributes(request, stacktrace);

        return new ResponseEntity<Map<String, Object>>(r, getStatus(request));
    }*/

    /**
     * Extracts the response content type from the "Accept" HTTP header field.
     *
     * @param request request instance
     *
     * @return response content type
     */
    private MediaType getResponseType(HttpServletRequest request)
    {
        String accepts = request.getHeader(HttpHeaders.ACCEPT);

        // only XML or JSON allowed
        if(accepts.contains(MediaType.APPLICATION_JSON_VALUE))
            return MediaType.APPLICATION_JSON_UTF8;
        else return MediaType.APPLICATION_XML;
    }
}

就是这样,POJO WebErrorResponse是一个仅使用公共字符串和int字段的普通类。

上面的类适用于支持XML和JSON的REST API。工作原理:

  • 来自控制器(自定义逻辑和应用程序逻辑)的异常将由Spring异常处理程序处理
  • 来自Spring的异常将由Spring异常处理程序处理(例如缺少参数)
  • 404(缺少endpoint)将由Spring Boot error Controller
  • 处理
  • mimetype问题(例如,请求镜像/PNG但引发异常)将首先移动到Spring Expetion处理程序,然后重定向到Spring Boot错误控制器(由于mimetype异常)
 类似资料:
  • 我们正在为我们的一个新项目探索Drools BPM。我基本上来自。NET背景,对java技术没有太多的知识。 我们期望能够使用Drools创建规则和工作流,以便将其公开为REST API,然后使用REST从。NET/Angular客户机应用程序执行规则。 我已经使用WildFly10服务器配置了KIE服务器。我的所有配置似乎都工作正常,但当我试图连接到控制器(“http://localhost:8

  • ----在2019-06-03@13:21 CET下面添加信息----因为我有一个普通的Spring(非引导)应用程序接受来自打印机的POST请求,所以我能够在传入的请求中记录信息。所以我就这么做了。 这是来自打印机的POST请求之一,Spring boot Oontroller不接受它: 这是Postman向完全相同的URL发送的POST请求之一,Spring boot Oontroller接受

  • 我正在尝试构建一个Spring WebFlux项目,并实现以下业务逻辑: 1-使用WebClient调用外部REST Api,并使用下面的模型解析Json结果。它工作正常 谢谢

  • 我有一个spring应用程序,它可以与Mobile交换JSON。Spring控制器如下所示: 我想知道,记录http请求正文和响应正文的最佳方式是什么?目前,我有一个定制的json消息转换器,它在从json中创建bean之前记录一个请求正文。我使用CustomTraceInterceptor记录响应正文。不幸的是,CustomTraceInterceptor不允许记录请求正文。 任何更好的解决方案

  • 我使用mn create-app example.micronaut.complete创建了一个新的micronaut应用程序 之后,我使用intellij打开了项目,并将一个新类作为TestController添加到项目中,代码如下: 但我得到了

  • 我有一个使用Spring MVC 3.1.3的应用程序和使用Dojo1.4开发的UI。应用程序只有很少的控制器处理通过上传的二进制文件。控制器发送一个json响应,该响应必须用 我实现了web.xml中定义的自定义筛选器: 提前道谢。 更新:我禁用了Spring Security性,并用普通spring mvc进行了测试,但问题仍然存在。我修改了标题和问题描述。