Spring: 如何选择运行时间的回复类型?
Spring: How to choose response type on run time?
我想在方法中选择 运行 时间的响应媒体类型。
例如下面的代码:
@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种。
- byte[](按正常执行路径)
- MyDataException(按异常执行路径)
MyDataException 稍后由异常处理程序处理,并转换为简单的 class。它可以转换为 json 响应。
首先,我认为如果我为 @RequestMapping
注释的 produces
选项提供 2 种响应类型,消息转换器将根据实际的 return 对象转换这两种类型.但事实并非如此。
在 spring class org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor
中,writeWithMessageConverters()
方法在选择响应类型时忽略实际的 return 对象类型,如果 produces
选项存在。
如何让Spring根据实际return对象选择运行时间的响应类型?
自答。
- 移除
produces
.
- 将 return 类型更改为
ResponseEntity<byte[]>
。
Return如下:
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.
我引用了 Whosebug 的回答
为了这。另请参阅 Kumar Sambhav 关于设置异常处理程序的回答。
如果几天后没有更好的答案发布,我会选择这个答案。
我建议您在 Spring MVC 处理程序中使用 @ControllerAdvice 注释来处理异常。这是非常优雅的方式(实际上有 3 种方法可以移出异常处理问题)分离错误处理问题,例如设置适当的 HTTP 响应代码(2xx 以外的东西)和发回错误消息 /object。
有一个很棒的博客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();
}
}
处理程序的 return 类型也可以是 ModelAndView 对象,它将错误对象传递给您的视图层。
有关详细信息,请参阅博客。
另一种可能性是采用上述两种解决方案的混合方法:
@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);
}}
提出的解决方案
编辑:
我还添加了博客中的 FieldError 和 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; }}
我想在方法中选择 运行 时间的响应媒体类型。
例如下面的代码:
@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种。
- byte[](按正常执行路径)
- MyDataException(按异常执行路径)
MyDataException 稍后由异常处理程序处理,并转换为简单的 class。它可以转换为 json 响应。
首先,我认为如果我为 @RequestMapping
注释的 produces
选项提供 2 种响应类型,消息转换器将根据实际的 return 对象转换这两种类型.但事实并非如此。
在 spring class org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor
中,writeWithMessageConverters()
方法在选择响应类型时忽略实际的 return 对象类型,如果 produces
选项存在。
如何让Spring根据实际return对象选择运行时间的响应类型?
自答。
- 移除
produces
. - 将 return 类型更改为
ResponseEntity<byte[]>
。 Return如下:
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.
我引用了 Whosebug 的回答 为了这。另请参阅 Kumar Sambhav 关于设置异常处理程序的回答。
如果几天后没有更好的答案发布,我会选择这个答案。
我建议您在 Spring MVC 处理程序中使用 @ControllerAdvice 注释来处理异常。这是非常优雅的方式(实际上有 3 种方法可以移出异常处理问题)分离错误处理问题,例如设置适当的 HTTP 响应代码(2xx 以外的东西)和发回错误消息 /object。
有一个很棒的博客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();
}
}
处理程序的 return 类型也可以是 ModelAndView 对象,它将错误对象传递给您的视图层。
有关详细信息,请参阅博客。
另一种可能性是采用上述两种解决方案的混合方法:
@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);
}}
提出的解决方案
编辑:
我还添加了博客中的 FieldError 和 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; }}