在Spring Controller方法调用上填充对象

时间:2013-09-20 20:22:47

标签: java spring-mvc

修改我添加了更多详细信息以帮助其他人并将原始问题保留为历史记录

背景我已经制作了一个REST调用原型,该调用在与我的客户端软件一起使用的Spring Controller中返回JSON。客户端软件具有查询数据的特定方式。该查询与我的Spring代码不兼容,所以我有几行进行转换。我将转换代码重构为自己的对象。我不想在需要它的REST方法中创建每次,而是希望在它到达我的方法之前预先填充它。

问题在Spring Controller中我可以让Spring从URL和标题中的值预先填充一个对象,类似于Spring如何从表单填充和对象?

当前代码

@RequestMapping(value="", headers = "Accept=application/json", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<String> searchUserProjects(
          @RequestParam(required = false) String projectName, 
          @RequestParam(required = false) String sortBy, 
          @RequestHeader(value = "Range") String range) {

原始问题我知道在Spring中你可以获取表单的属性并将它们映射到一个对象。另外,我知道你可以将一个字段映射到属性转换器对象,我不记得确切的名称,但我已经完成了。我的问题是,是否可以让Spring从URL和标题中的值填充对象,然后将其传递给方法,而不是在控制器的方法签名处声明它们?

修改

applicationContext.xml中的注册方法

<mvc:annotation-driven>
    <mvc:argument-resolvers>
        <bean class="app.util.dojo.DojoQueryProcessorHandlerMethodArgumentResolver"/>
    </mvc:argument-resolvers>
</mvc:annotation-driven>

带参数

的处理程序方法
public ResponseEntity<String> searchUserProjects(@RequestParam(required = false) String projectName, @ProcessDojoQuery DojoRestQueryProcessor dojoQueryResults) {

DojoRestQueryProcessor.java

package app.util.dojo;

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;

public class DojoRestQueryProcessor {

    protected String[] rangeArray;
    protected String range;
    protected String sortBy;
    protected int startIndex;
    protected int endIndex;

    public DojoRestQueryProcessor() {
    }
    public DojoRestQueryProcessor(String range, String sortBy) {
        if (range== null && sortBy == null)
            return;
        if (range.length() <= 3 || !range.contains("-"))
            throw new DojoRestQueryProcessorException("Range value does not meet spec. " + range);
        this.rangeArray = range.substring(6).split("-");
        this.range = range;
        this.sortBy = sortBy;
    }

    public PageRequest createPageRequest() {
        startIndex = Integer.parseInt(rangeArray[0]);
        endIndex = Integer.parseInt(rangeArray[1]);
        if (startIndex >= endIndex)
            throw new IllegalArgumentException("The starting index for a range needs to be less than the end index.");

        Sort.Order[] sortOrders = null;
        if (sortBy != null && sortBy.length() > 2)
            sortOrders = convertDojoSortValuesToSpringSorts(sortBy.split(","));
        int pageSize = endIndex-startIndex+1;
        int pageNum = ((endIndex+1)/pageSize)-1;
        PageRequest pageRequest = null;
        if (sortOrders != null)
            pageRequest = new PageRequest(pageNum, pageSize, new Sort(sortOrders));
        else 
            pageRequest = new PageRequest(pageNum, pageSize);
        return pageRequest;
    }

    public static Sort.Order[] convertDojoSortValuesToSpringSorts(String[] sortStrings) {
        if (sortStrings == null)
            return null;
        Sort.Order[] sortOrders = new Sort.Order[sortStrings.length];
        for (int i = 0; i < sortStrings.length; i++) {
            String sortString = sortStrings[i];
            if (sortString.startsWith("-")) {
                sortOrders[i] = new Sort.Order(Direction.DESC, sortString.substring(1));
            } else {
                sortOrders[i] = new Sort.Order(Direction.ASC, sortString.substring(1));
            }
        }
        return sortOrders;
    }

    public int getStartIndex() {
        return startIndex;
    }

    public int getEndIndex() {
        return endIndex;
    }

    public String getRange() {
        return range;
    }

    public String getSortBy() {
        return sortBy;
    }


}

我的方法处理程序:

package app.util.dojo;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerMapping;

public class DojoQueryProcessorHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {


    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(ProcessDojoQuery.class) && parameter.getParameterType().equals(DojoRestQueryProcessor.class) ;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
            throws Exception {
        String rangeField = parameter.getParameterAnnotation(ProcessDojoQuery.class).rangeField();
        String sortByField = parameter.getParameterAnnotation(ProcessDojoQuery.class).sortByField();

        String range = getRangeValue(rangeField, webRequest);
        String sortBy = getSortByValue(sortByField, webRequest);
        return new DojoRestQueryProcessor(range, sortBy);
    }

    private String getSortByValue(String rangeField, NativeWebRequest webRequest) {
        Map<String, String> pathVariables = getPathVariables(webRequest);
        return pathVariables.get(rangeField);
    }

    private Map<String, String> getPathVariables(NativeWebRequest webRequest) {
        HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        return (Map<String, String>) httpServletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
    }

    private String getHeaderValue(String headerName, NativeWebRequest webRequest) {
        HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        return httpServletRequest.getHeader(headerName);
    }

    private String getRangeValue(String rangeField, NativeWebRequest webRequest) {
        return getHeaderValue(rangeField, webRequest);
    }

}

2 个答案:

答案 0 :(得分:2)

这是可能的,但你必须自己做(一次)。

此界面为HandlerMethodArgumentResolver。我看到它的方式是你会创建一个注释,比如@FromUrlAndHeaders,并使用它来注释方法中的参数:

@RequestMapping(value = "/someRequest/path")
public String doBusiness(@FromUrlAndHeaders CustomObject customObject) {
    // do business with customObject
}

然后有趣的部分是创建自己的HandlerMethodArgumentResolver

public class FromUrlAndHeadersHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(FromUrlAndHeaders.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter,
                ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        // use the various objects here
        // request to get parameters and headers
        // mavContainer for model attributes (if you need) 
        // parameter for class type and annotation attributes
        // etc.     

        // note that the parameter class type matters, are your creating a CustomObject, a String, a DifferentClassObject, etc...
    }
}

然后,您可以注册此HandlerMethodArgumentResolver并让它发挥作用。


DispatcherServlet堆栈使用HandlerMethodArgumentResolver实现实例列表来决定传递给方法的参数。 @ModelAttribute@PathVariable@RequestParam@RequestBodyModelMapHttpServletRequestHttpServletResponse,{{1}}为{{1}}。 ,基本上是默认支持的每种参数类型。您可以在javadoc中看到所有这些内容。

相关:

答案 1 :(得分:0)

也许我没有得到你的问题,这不是你想要的,但如果你想在动作方法中注入所有参数,只需将其声明为:

@RequestMapping(method = { RequestMethod.POST })
    public ResponseEntity doSomethingCool(@RequestParam Map<String, String> parameters) {
...
}