Return HTTP 204 on null with spring @RestController
Return HTTP 204 on null with spring @RestController
这个 returns 200 OK 内容长度:0
@RestController
public class RepoController {
@RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
public Object getDocument(@PathVariable long id) {
return null;
}
}
简而言之,我想 return 204 No Content on null。
有没有办法在 null 而不是 200 时将 spring-mvc/rest 强制为 return 204?我不想将每个休息方法更改为 return ResponseEntity 或类似的东西,只将 null 映射到 204
当然可以。
选项 1:
@RestController
public class RepoController {
@RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
public Object getDocument(@PathVariable long id, HttpServletResponse response) {
Object object = getObject();
if( null == object ){
response.setStatus( HttpStatus.SC_NO_CONTENT);
}
return object ;
}
}
选项 2:
@RestController
public class RepoController {
@RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
public Object getDocument(@PathVariable long id) {
Object object = getObject();
if ( null == object ){
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
return object ;
}
}
可能有错别字,但你明白了。
你可以试试这个:
@RestController
public class RepoController {
@RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
public ResponseEntity<String> getDocument(@PathVariable long id) {
if(noError) {
............
return new ResponseEntity<String>(HttpStatus.OK);
}
else {
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
}
}
}
您需要将 HttpStatus.BAD_REQUEST 更改为等效的 204 代码状态
您可以使用 @ResponseStatus 注释。这样你就可以拥有一个 void 方法,而不必构建 ResponseEntity。
@DeleteMapping(value = HERO_MAPPING)
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long heroId) {
heroService.delete(heroId);
}
顺便说一句,当对象存在时 returning 200,否则 204 API REST 设计有点不寻常。当找不到请求的对象时,return 404(未找到)很常见。这可以使用 ControllerAdvice 来实现。
在 Spring REST 中,最好使用异常处理程序来处理异常,而不是放置逻辑来决定响应状态等。这是一个使用 @ControllerAdvice 注释的示例:http://www.jcombat.com/spring/exception-handling-in-spring-restful-web-service
相同的答案,但由 AOP 解决:
@Aspect
public class NoContent204HandlerAspect {
@Pointcut("execution(public * xx.xxxx.controllers.*.*(..))")
private void anyControllerMethod() {
}
@Around("anyControllerMethod()")
public Object handleException(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
Optional<HttpServletResponse> response = Arrays.asList(args).stream().filter(x -> x instanceof HttpServletResponse).map(x -> (HttpServletResponse)x).findFirst();
if (!response.isPresent())
return joinPoint.proceed();
Object retVal = joinPoint.proceed();
if (retVal == null)
response.get().setStatus(HttpStatus.NO_CONTENT.value());
return retVal;
}
}
我用过滤器解决了这个问题。它是全球性的和简单的。
package your.package.filter;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class NoContentFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(httpServletRequest, httpServletResponse);
if (httpServletResponse.getContentType() == null ||
httpServletResponse.getContentType().equals("")) {
httpServletResponse.setStatus(HttpStatus.NO_CONTENT.value());
}
}
}
并在您的 web.xml
中添加以下内容
<filter>
<filter-name>restNoContentFilter</filter-name>
<filter-class>your.package.filter.NoContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>restNoContentFilter</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
问题是旧问题,但对于那些需要全局答案并拥有 Spring 4+ 的问题,您可以创建一个 ResponseBodyAdvice,根据控制器响应更改响应代码。以下示例对所有 @RestController 类 执行此操作:
@ControllerAdvice(annotations = { RestController.class })
public class NullToNoContentResponseBodyAdvice
implements ResponseBodyAdvice<Object>
{
/**
* {@inheritDoc}
*/
@Override
public Object beforeBodyWrite(final Object p_responseBodyObject, final MethodParameter p_methodParameter,
final MediaType p_mediaType, final Class<? extends HttpMessageConverter<?>> p_class,
final ServerHttpRequest p_serverHttpRequest,
final ServerHttpResponse p_serverHttpResponse)
{
// ------------------------- DECLARE -------------------------- //
if (p_responseBodyObject == null)
{
p_serverHttpResponse.setStatusCode(HttpStatus.NO_CONTENT);
}
// Always return object unchanged or it will break response
return p_responseBodyObject;
}
/**
* {@inheritDoc}
*/
@Override
public boolean supports(final MethodParameter p_methodParameter, final Class<? extends HttpMessageConverter<?>> p_class)
{
return AbstractGenericHttpMessageConverter.class.isAssignableFrom(p_class);
}
}
您可以自定义 HttpMessageConverter
来支持这一点,就像我这样做一样,将 spring.http.converters.preferred-json-mapper=gson
添加到 application.properties
配置文件,结果建议如 :
@ControllerAdvice
public class CommonResultAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (null == o) {
//set http code
serverHttpResponse.setStatusCode(HttpStatus.NO_CONTENT);
return BaseResult.success();
}
if (o instanceof String) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(BaseResult.success(o));
} catch (JsonProcessingException ignore) {
}
}
if (o instanceof BaseResult) {
return o;
}
return BaseResult.success(o);
}
}
或像这样自定义 HttpMessageConverter
:
@Configuration
public class BeanConfiguration {
@Bean
public HttpMessageConverter resultToJsonConverter() {
return new GsonHttpMessageConverter();
}
}
希望能帮到你。 :)
这个 returns 200 OK 内容长度:0
@RestController
public class RepoController {
@RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
public Object getDocument(@PathVariable long id) {
return null;
}
}
简而言之,我想 return 204 No Content on null。
有没有办法在 null 而不是 200 时将 spring-mvc/rest 强制为 return 204?我不想将每个休息方法更改为 return ResponseEntity 或类似的东西,只将 null 映射到 204
当然可以。
选项 1:
@RestController
public class RepoController {
@RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
public Object getDocument(@PathVariable long id, HttpServletResponse response) {
Object object = getObject();
if( null == object ){
response.setStatus( HttpStatus.SC_NO_CONTENT);
}
return object ;
}
}
选项 2:
@RestController
public class RepoController {
@RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
public Object getDocument(@PathVariable long id) {
Object object = getObject();
if ( null == object ){
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
return object ;
}
}
可能有错别字,但你明白了。
你可以试试这个:
@RestController
public class RepoController {
@RequestMapping(value = "/document/{id}", method = RequestMethod.GET)
public ResponseEntity<String> getDocument(@PathVariable long id) {
if(noError) {
............
return new ResponseEntity<String>(HttpStatus.OK);
}
else {
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
}
}
}
您需要将 HttpStatus.BAD_REQUEST 更改为等效的 204 代码状态
您可以使用 @ResponseStatus 注释。这样你就可以拥有一个 void 方法,而不必构建 ResponseEntity。
@DeleteMapping(value = HERO_MAPPING)
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long heroId) {
heroService.delete(heroId);
}
顺便说一句,当对象存在时 returning 200,否则 204 API REST 设计有点不寻常。当找不到请求的对象时,return 404(未找到)很常见。这可以使用 ControllerAdvice 来实现。
在 Spring REST 中,最好使用异常处理程序来处理异常,而不是放置逻辑来决定响应状态等。这是一个使用 @ControllerAdvice 注释的示例:http://www.jcombat.com/spring/exception-handling-in-spring-restful-web-service
相同的答案,但由 AOP 解决:
@Aspect
public class NoContent204HandlerAspect {
@Pointcut("execution(public * xx.xxxx.controllers.*.*(..))")
private void anyControllerMethod() {
}
@Around("anyControllerMethod()")
public Object handleException(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
Optional<HttpServletResponse> response = Arrays.asList(args).stream().filter(x -> x instanceof HttpServletResponse).map(x -> (HttpServletResponse)x).findFirst();
if (!response.isPresent())
return joinPoint.proceed();
Object retVal = joinPoint.proceed();
if (retVal == null)
response.get().setStatus(HttpStatus.NO_CONTENT.value());
return retVal;
}
}
我用过滤器解决了这个问题。它是全球性的和简单的。
package your.package.filter;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class NoContentFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(httpServletRequest, httpServletResponse);
if (httpServletResponse.getContentType() == null ||
httpServletResponse.getContentType().equals("")) {
httpServletResponse.setStatus(HttpStatus.NO_CONTENT.value());
}
}
}
并在您的 web.xml
中添加以下内容<filter>
<filter-name>restNoContentFilter</filter-name>
<filter-class>your.package.filter.NoContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>restNoContentFilter</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
问题是旧问题,但对于那些需要全局答案并拥有 Spring 4+ 的问题,您可以创建一个 ResponseBodyAdvice,根据控制器响应更改响应代码。以下示例对所有 @RestController 类 执行此操作:
@ControllerAdvice(annotations = { RestController.class })
public class NullToNoContentResponseBodyAdvice
implements ResponseBodyAdvice<Object>
{
/**
* {@inheritDoc}
*/
@Override
public Object beforeBodyWrite(final Object p_responseBodyObject, final MethodParameter p_methodParameter,
final MediaType p_mediaType, final Class<? extends HttpMessageConverter<?>> p_class,
final ServerHttpRequest p_serverHttpRequest,
final ServerHttpResponse p_serverHttpResponse)
{
// ------------------------- DECLARE -------------------------- //
if (p_responseBodyObject == null)
{
p_serverHttpResponse.setStatusCode(HttpStatus.NO_CONTENT);
}
// Always return object unchanged or it will break response
return p_responseBodyObject;
}
/**
* {@inheritDoc}
*/
@Override
public boolean supports(final MethodParameter p_methodParameter, final Class<? extends HttpMessageConverter<?>> p_class)
{
return AbstractGenericHttpMessageConverter.class.isAssignableFrom(p_class);
}
}
您可以自定义 HttpMessageConverter
来支持这一点,就像我这样做一样,将 spring.http.converters.preferred-json-mapper=gson
添加到 application.properties
配置文件,结果建议如 :
@ControllerAdvice
public class CommonResultAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (null == o) {
//set http code
serverHttpResponse.setStatusCode(HttpStatus.NO_CONTENT);
return BaseResult.success();
}
if (o instanceof String) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(BaseResult.success(o));
} catch (JsonProcessingException ignore) {
}
}
if (o instanceof BaseResult) {
return o;
}
return BaseResult.success(o);
}
}
或像这样自定义 HttpMessageConverter
:
@Configuration
public class BeanConfiguration {
@Bean
public HttpMessageConverter resultToJsonConverter() {
return new GsonHttpMessageConverter();
}
}
希望能帮到你。 :)