Spring:如何在运行时选择响应类型?

时间:2015-05-14 02:57:22

标签: spring spring-mvc spring-boot

我想在方法中选择运行时的响应媒体类型。

例如,以下代码:

@RequestMapping(value = "/getRecord",
    produces = {"application/octet-stream", "application/json;charset=UTF-8" })
public byte[] getData(
    @RequestParam(value="id", required=true) Integer id)
    throws IOException
{
    if (id == 1)
        return createByteArray();
    throw new MyDataException();
}

在此代码中,可能的响应类型实际上是2。

  1. byte [](通过正常执行路径)
  2. MyDataException(通过异常执行路径)
  3. MyDataException稍后由异常处理程序处理,并转换为简单类。它可以转换为json响应。

    首先,我认为如果我为produces注释的@RequestMapping选项提供2种响应类型,则消息转换器将根据实际的返回对象转换2种类型。但事实并非如此。

    在spring类org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor中,writeWithMessageConverters()方法在选择响应类型时忽略实际的返回对象类型,如果存在produces选项。

    如何让Spring根据实际的返回对象选择运行时的响应类型?

3 个答案:

答案 0 :(得分:1)

自的答案。

  • 删除produces
  • 将退货类型更改为ResponseEntity<byte[]>
  • 返回如下:

    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    return new ResponseEntity<byte[]>(createByteArray(), responseHeaders, HttpStatus.OK);
    

结果,问题的代码转换如下:

@RequestMapping(value = "/getRecord")
public ResponseEntity<byte[]> getData(@RequestParam(value="id", required=true) Integer id)
    throws IOException
{
    if (id == 1)
    {
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        return new ResponseEntity<byte[]>(createByteArray(), responseHeaders, HttpStatus.OK);
    }
    throw new MyDataException();
}

现在响应类型如下:

  • 在正常执行路径上,appliaction / octet-stream。
  • ON异常执行路径,application / json。

我引用了StackOverflow回答https://stackoverflow.com/a/4483387/3004042 为了这。另请参阅Kumar Sambhav关于设置异常处理程序的答案。

如果几天内没有更好的答案,我会选择这个答案。

答案 1 :(得分:1)

我建议你使用@ControllerAdvice注释来处理Spring MVC处理程序中的异常。这是非常优雅的方式(实际上有3种方法可以解决您的异常处理问题)分离错误处理问题,例如设置适当的HTTP响应代码(2xx以外的其他代码)和发回错误消息/对象。

有一个很棒的博客here

示例(从Spring博客借来): -

@ControllerAdvice
class GlobalControllerExceptionHandler {
    @ResponseStatus(HttpStatus.CONFLICT)  // 409
    @ExceptionHandler(DataIntegrityViolationException.class)
    public void handleConflict() {
        // Nothing to do
    }
}

在你的情况下,我建议采用@ControllerAdvice方法,如: -

@ControllerAdvice
class GlobalControllerExceptionHandler {
    @ResponseStatus(HttpStatus.CONFLICT)  // 409
    @ResponseBody
    @ExceptionHandler(MyDataException.class)
    public AnyReturnType handleConflict(Exception exception) {
         return exception.getDetails();
    }
}

处理程序的返回类型也可以是ModelAndView对象,它将错误对象传递给视图层。

请参阅博客了解更多详情。

答案 2 :(得分:0)

另一种可能性是两种所述解决方案之间的混合方法:

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

@ExceptionHandler({ MyDataException.class })
protected ResponseEntity<Object> handleInvalidRequest(RuntimeException e, WebRequest request) {
    MyDataExceptionire = (MyDataException) e;
    List<FieldErrorResource> fieldErrorResources = new ArrayList<>();

    List<FieldError> fieldErrors = ire.getErrors().getFieldErrors();
    for (FieldError fieldError : fieldErrors) {
        FieldErrorResource fieldErrorResource = new FieldErrorResource();
        fieldErrorResource.setResource(fieldError.getObjectName());
        fieldErrorResource.setField(fieldError.getField());
        fieldErrorResource.setCode(fieldError.getCode());
        fieldErrorResource.setMessage(fieldError.getDefaultMessage());
        fieldErrorResources.add(fieldErrorResource);
    }

    ErrorResource error = new ErrorResource("MyDataException", ire.getMessage());
    error.setFieldErrors(fieldErrorResources);

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

    return handleExceptionInternal(e, error, headers, HttpStatus.UNPROCESSABLE_ENTITY, request);
}}

this blog

上提出的解决方案

修改

我还在博客中添加了FieldError和ErrorResource类,因为它可能会在将来被删除:

<强> ErrorResource:

@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorResource {
private String code;
private String message;
private List<FieldErrorResource> fieldErrors;

public ErrorResource() { }

public ErrorResource(String code, String message) {
    this.code = code;
    this.message = message;
}

public String getCode() { return code; }

public void setCode(String code) { this.code = code; }

public String getMessage() { return message; }

public void setMessage(String message) { this.message = message; }

public List<FieldErrorResource> getFieldErrors() { return fieldErrors; }

public void setFieldErrors(List<FieldErrorResource> fieldErrors) {
    this.fieldErrors = fieldErrors;
}
}

<强> FieldErrorResource:

@JsonIgnoreProperties(ignoreUnknown = true)
public class FieldErrorResource {
private String resource;
private String field;
private String code;
private String message;

public String getResource() { return resource; }

public void setResource(String resource) { this.resource = resource; }

public String getField() { return field; }

public void setField(String field) { this.field = field; }

public String getCode() { return code; }

public void setCode(String code) { this.code = code; }

public String getMessage() { return message; }

public void setMessage(String message) { this.message = message; }}