1

编辑我添加了更多细节以帮助他人,并将原始问题留作历史

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

问题在 Spring 控制器中,我是否可以让 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);
    }

}
4

2 回答 2

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决定传递给您的方法的参数。有一个 for @ModelAttribute, for @PathVariable, for @RequestParam, for @RequestBody, for ModelMap, for HttpServletRequest, for HttpServletResponse,基本上是默认支持的每种参数类型。您可以在 javadoc 中看到所有这些。

有关的:

于 2013-09-20T20:36:49.180 回答
0

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

@RequestMapping(method = { RequestMethod.POST })
    public ResponseEntity doSomethingCool(@RequestParam Map<String, String> parameters) {
...
}
于 2013-09-20T20:32:57.730 回答