Spring中的@RequestBody vs HandlerMethodArgumentResolver

时间:2016-06-13 08:00:21

标签: spring

对于同样的问题,我有两种解决方案,但我不确定从长远来看哪种解决方案更好。

解决方案1:

控制器是:

@RequestMapping(value = "/skill/leatherworking/curing/start", method = RequestMethod.POST)
public Response startCuring(final UserEntity userEntity, @RequestBody @Valid final CuringCreateRequest curingCreateRequest) {
    final CuringResult result = curingService.cure(userEntity, recipeDefinitionCache.getDefinition(curingCreateRequest.getRecipeId()));
    ...
}

域对象是:

public class CuringCreateRequest {

    @Min(1)
    private int recipeId;

    ...
}

解决方案2:

控制器是:

@RequestMapping(value = "/skill/leatherworking/curing/start", method = RequestMethod.POST)
public Response startCuring(final UserEntity userEntity, @Valid final CuringCreateRequest curingCreateRequest) {
    final CuringResult result = curingService.cure(userEntity, curingCreateRequest.getRecipe());
    ...
}

这里我们还有一个HandlerMethodArgumentResolver:

public class RequestContextHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean supportsParameter(final MethodParameter parameter) {
        return parameter.getParameterType().equals(CuringCreateRequest.class);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer, final NativeWebRequest webRequest,
        final WebDataBinderFactory binderFactory) throws Exception {
        ... //Manually resolve the argument via JsonMapper and RecipeDefinitionCache. This way we can create an immutable class and that's a bonus too.
    }
}

域对象是:

public class CuringCreateRequest {

    @NotNull
    private RecipeDefinition recipe;

    ...
}

从长远来看哪种解决方案更好用?特别是在中型或大型项目中?我更喜欢解决方案2 ,因为它更干净,控制器的责任性更低,但不能确定它的出路是否足以为每个不同的参数创建另一个类的麻烦。特别是@RequestBody可以在2秒内解决问题。

1 个答案:

答案 0 :(得分:2)

我建议使用选项2.可以增强此功能的一种方法是在对象的setter方法中添加编程验证方法,并在实现的HandlerMethodArgumentResolver中调用setter方法。

e.g。

之类的东西
public class Foo {
    private String bar;
    ...
    public void setBar (String bar) {
        //disallow special characters
        if ( !StringUtils.isAlphanumeric(bar)
            this.bar = null;
        else
            this.bar = bar;
    }
}

然后在你的自定义HandlerMethodArgumentResolver中使用所述的setter:

public class CustomFooHandlerMethodArgumentResolver extends HandlerMethodArgumentResolver {
   @Override
   public boolean supportsParameter (MethodParameter methodParameter) {
        return methodParameter.getParameterType().equals(Foo.class);
    }

   @Override
   public Object resolveArgument (MethodParameter methodParam, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {

        Foo myFoo = new Foo();
        myFoo.setBar(webRequest.getParameter("bar"));
        ...
        return myFoo;
    }
}

通过这种实现,您只需要一个中心验证点(setter方法),并且可以防止在端点和整个程序中设置字段中的非法值。

另一个想法是你可以在setter中添加比注释更复杂的验证逻辑,包括进行额外的方法调用来处理不允许的值(或者获取默认值的其他方法调用等)

底线,没有正确的方法,取决于项目的范围,但您可能可能与选项2一样严格,而您只需稍微限制(但运行更快)选项1