在编组响应之前拦截null ResponseBody

时间:2015-12-28 15:42:16

标签: java spring-mvc

我有多个RESTful端点控制器,如果端点没有资源,它们当前返回null。例如,

@RequestMapping(method = ReqeustMethod.GET, value = "{id}")
@ResponseBody
public MyResource get(@PathVariable final Long id) {
    return this.myService.get(id); // returns null if bad id
}

当没有ErrorResource具有给定ID时,我想向客户端(MyResource)返回特定的不同资源。我知道我可以使用@ExceptionHandler的单独方法来做到这一点,例如:

@RequestMapping(method = RequestMethod.GET, value = "{id}")
@ResponseBody
public MyResource get(@PathVariable final Long id) {
    final MyResource myResource = this.myService.get(id);
    if (myResource == null) {
        throw new NotFoundException();
    }
    return myResource;
}

@ExceptionHandler(NotFoundException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResource notFoundException(
        final HttpServletRequest request,
        final NotFoundException exception) {

    final ErrorResource errorResource = new ErrorResource();
    errorResource.setStatus(HttpStatus.NOT_FOUND.value());
    errorResource.setDeveloperMessage("No resource found at " + request.getRequestURL());
    return errorResource;
}

这很好。但是我真正希望能够做的是有一种拦截器,它可以告诉我,每当API方法返回null @ResponseBody时,它应该在我的运行中运行逻辑notFoundException()方法。这将使我的所有控制器方法更清洁。有没有办法做到这一点?

1 个答案:

答案 0 :(得分:0)

这听起来像是Spring HttpMessageConverter的工作。

您可以通过实施HttpMessageConverter<T>界面编写自己的转换器。

在您的情况下,我将实现HttpMessageConverter<MyResource>的转换器,并对MyResource方法中的write实例进行空检查。如果MyResource实例为null,则构建并编写ErrorResource实例。

以下是一个例子:

import java.io.IOException;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

public class MyResourceConverter implements HttpMessageConverter<MyResource> {

    // a real message converter that will respond to ancillary methods and do the actual work
    private HttpMessageConverter<Object> delegateConverter;

    public MyResourceConverter(HttpMessageConverter<Object> delegateConverter){
        this.delegateConverter = delegateConverter;
    }

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return delegateConverter.canRead(clazz, mediaType) && MyResource.class.equals(clazz);
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return delegateConverter.canWrite(clazz, mediaType) && MyResource.class.equals(clazz);
    }

    @Override
    public MyResource read(Class<? extends MyResource> clazz,
            HttpInputMessage inputMessage) throws IOException,
            HttpMessageNotReadableException {
        return (MyResource) delegateConverter.read(clazz, inputMessage);
    }

    @Override
    public void write(MyResource t, MediaType contentType,
            HttpOutputMessage outputMessage) throws IOException,
            HttpMessageNotWritableException {
        Object result = null;
        if(t == null){
            result = // build your ErrorResource here
        }else{
            result = t;
        }
        delegateConverter.write(result, contentType, outputMessage);

    }
}

请注意,此转换器需要在Spring配置中注册。 配置类必须扩展WebMvcConfigurerAdapter并覆盖configureMessageConverters方法,如:

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    // Here we add our custom-configured HttpMessageConverters.
    // yourDelegateConverter may be a MappingJackson2HttpMessageConverter instance for example
    converters.add(new EmployeeConverter(yourDelegateConverter));
    super.configureMessageConverters(converters);
}

参考文献(来自官方Spring文档):