在Spring MVC 4.0中自动转换JSON表单参数

Jon*_*ahl 9 java spring json spring-mvc jackson

我正在尝试构建一个Spring MVC控制器,它将接收带有JSON格式参数的POSTed表单,并让Spring自动将其转换为Java对象.

  • 请求内容类型是 application/x-www-form-urlencoded
  • 包含JSON字符串的参数的名称是 data.json

这是控制器:

@Controller
public class MyController {
    @RequestMapping(value = "/formHandler", method = RequestMethod.POST)
    public @ResponseBody String handleSubscription(
        @RequestParam("data.json") MyMessage msg) {
        logger.debug("id: " + msg.getId());
        return "OK";
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是MyMessage对象的样子:

public class MyMessage {
    private String id;
    // Getter/setter omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

也许并不奇怪,发布带有参数data.json = {"id":"Hello"}的表单会导致HTTP错误500,并出现此异常:

org.springframework.beans.ConversionNotSupportedException:
    Failed to convert value of type 'java.lang.String' to required type 'MyMessage' 
nested exception is java.lang.IllegalStateException:
    Cannot convert value of type [java.lang.String] to required type [MyMessage]: no matching editors or conversion strategy found
Run Code Online (Sandbox Code Playgroud)

如果我正确读取MappingJackson2HttpMessageConverter文档,则由Content-Type触发Jackson JSON转换application/json,我显然无法使用,因为这是一个表单POST(我不控制POST部分).

是否有可能让Spring将JSON字符串转换为MyMessage的实例,或者我应该放弃,将其作为字符串读取并自己执行转换?

Sot*_*lis 18

Spring @RequestMapping使用反射调用您的方法.要解决它将传递给调用的每个参数,它使用的实现HandlerMethodArgumentResolver.对于带@RequestParam注释的参数,它使用RequestParamMethodArgumentResolver.此实现将请求参数绑定到单个对象,通常是一种String或某种Number类型.

但是,您的用例更为罕见.您很少收到json请求参数,这就是为什么我认为您应该重新考虑您的设计,但如果您没有其他选择,则需要注册一个自定义PropertyEditor,它将把请求参数的json值转换为您的自定义类型.

@InitBinder在您@Controller班级的注释方法中注册很简单

@InitBinder
public void initBinder(WebDataBinder dataBinder) {
    dataBinder.registerCustomEditor(MyMessage.class, new PropertyEditorSupport() {
        Object value;
        @Override
        public Object getValue() {
            return value;
        }

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            value = new Gson().fromJson((String) text, MyMessage.class);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

在这种特殊情况下,我们不需要PropertyEditor接口的所有方法,因此我们可以使用PropertyEditorSupport哪个是有用的默认实现PropertyEditor.我们只需实现我们关心的两种方法,即使用我们想要的任何JSON解析器.我用过,Gson因为它可用.

当Spring看到它有你请求的请求参数时,它将检查参数类型,找到类型MyMessage并查找为该类型注册PropertyEditor的.它会找到它,因为我们注册了它,然后它将使用它来转换值.

您可能需要实现其他方法,PropertyEditor具体取决于您接下来要做什么.

我的建议是永远不要将JSON作为请求参数发送.将您的请求内容类型设置为application/json并发送json作为请求正文.然后用@RequestBody它来解析它.

  • "我的建议是永远不要将JSON作为请求参数发送".我同意,但我不是控制发送方,我只是为第三方webhook实现接收器. (2认同)

小智 8

你也可以@RequestPart像这样使用:

@RequestMapping(value = "/issues", method = RequestMethod.POST, headers = "Content-Type=multipart/form-data")
public String uploadIssue(@RequestParam("image") MultipartFile file, @RequestPart("issue") MyMessage issue)
Run Code Online (Sandbox Code Playgroud)