Spring 4.x/3.x(Web MVC)REST API和JSON2 Post请求,如何一次性正确地完成?

43

在深入细节之前,我知道Stackoverflow上已经有很多关于此类的讨论和相关问题。这些都以不同的方式帮助了我,所以我想把我的发现汇总成一个有组织的FAQ,以总结我的发现。

相关概念

当然,你肯定知道这些,但我只是简单地回顾一下。如果我漏掉了什么,请随意编辑。

HTTP POST请求:

当您想要向Web服务或服务器端应用程序发送对象时,使用POST请求。

序列化:

是将对象从Web浏览器传递到服务器端应用程序的过程。可以使用jQuery Ajax调用或Curl post请求。

序列化协议:

目前最流行的是JSON和XML。由于XML标记的特性,序列化的XML对象相对较大,因此XML正在变得越来越不受欢迎。在本FAQ中,主要关注JSON2序列化。

Spring:

Spring框架及其强大的注释使得以高效的方式暴露Web服务成为可能。Spring中有许多不同的库,我们关注的是Spring Web MVC

Curl vs JQuery:

这些工具可以用于在客户端进行POST请求。即使您计划使用JQuery ajax调用,我建议您在调试时使用Curl,因为它可以在发出POST请求后提供详细的响应。

@RequestBody vs @RequestParam/@PathVariable vs @ModelAttribute:

在您有一个不依赖于Java EE模型的Web服务的情况下,必须使用@RequestBody。如果您正在使用模型并且您的JSON对象已添加到模型中,则可以通过@ModelAttribute访问该对象。仅在您的请求为GET请求或GET和POST请求组合的情况下,您才需要使用@RequestParam / @PathVariable。

@RequestBody vs @ResposeBody:

从名称中可以看出,只有在服务器端方法处理请求后向客户端发送响应时,才需要@ResponseBody。

RequestMappingHandlerAdapter与AnnotationMethodHandlerAdapter的区别:

RequestMappingHandlerAdapter是Spring框架中取代AnnotationMethodHandlerAdapter的新映射处理程序,自Spring 3.1以来如此。如果您现有的配置仍在使用AnnotationMethodHandlerAdapter,则可能会发现本文有用。我提供的配置将为您提供关于如何设置RequestMappingHandlerAdapter的想法。

设置

您需要设置消息转换器。这是将序列化的JSON消息体转换为服务器端的本地Java对象的方式。

基本配置请参考here。在basic configuration sample中,转换器为MarshallingHttpMessageConverter和CastorMarshaller,我已将它们替换为MappingJackson2HttpMessageConverter和MappingJacksonHttpMessageConverter。

配置放置位置

按照我的项目设置方式,我有两个配置文件:

  • 应用程序上下文XML: 其中一个应用程序上下文XML文件,其中包含您的sessionFactory bean、dataSource bean等。
  • MVC Dispatcher Servlet XML: 这是您拥有视图解析器bean并导入应用程序上下文XML的位置。

hadlerAdapter bean必须位于后面的MVC Dispatcher XML文件中。

<bean name="handlerAdapter"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
            <ref bean="jsonConverter"/>

        </list>

    </property>
    <property name="requireSession" value="false"/>

</bean>
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    <property name="supportedMediaTypes" value="application/json"/>
</bean>

你可以拥有多个消息转换器。在这里,我创建了一个普通的JSON和一个JSON 2消息转换器。XML文件中使用了Ref和普通bean格式(个人更喜欢ref标签,因为它更整洁)。

REST API

这是一个公开REST API的示例控制器。

控制器

这是公开HTTP POST请求的REST API的位置。

@Component
@Controller
@RequestMapping("/api/user")
public class UserController {
@RequestMapping(value = "/add", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String insertUser(@RequestBody final User user) {
    System.out.println(user.toString());
    userService.insertUser(user);
    String userAdded = "User-> {" + user.toString() + "} is added";
    System.out.println(userAdded);
        return userAdded;
    }
}

Java对象

@JsonAutoDetect
public class User {

private int id;
private String username;
private String name;
private String lastName;
private String email;

public int getId() {
    return externalId;
}

public void setId(final int id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(final String name) {
    this.name = name;
}

public String getEmail() {
    return email;
}

public void setEmail(final String email) {
    this.email = email;
}
public String getUsername() {
    return username;
}

public void setUsername(final String username) {
    this.username = username;
}

public String getLastName() {
    return lastName;
}

public void setLastName(final String lastName) {
    this.lastName = lastName;
}

@Override
public String toString() {
    return this.getName() + " | " + this.getLastName()  + " | " + this.getEmail()
            + " | " + this.getUsername()  + " | " + this.getId()  + " | ";
    }

}

CURL Post调用

curl -i -H "Content-Type: application/json" -X POST -d '{"id":100,"username":"JohnBlog","name":"John","lastName":"Blog","email":"JohnBlog@user.com"}' http://localhost:8080/[YOURWEBAPP]/api/user/add

相关文章和问题

如果没有以下人员提供的文章和问题(如果我发现有用的相关文章/问题,此列表将扩展),则无法编写此常见问题解答:

  1. 什么是正确的JSON内容类型?
  2. 使用jackson消息转换器在Spring 3.0中生成JSON响应
  3. 如何使用Curl从终端/命令行POST JSON数据以测试Spring REST?
  4. 发布JSON到REST API
  5. https://github.com/geowarin/spring-mvc-examples
  6. 如何使用curl将JSON发布到PHP?
  7. Spring REST | MappingJacksonHttpMessageConverter生成无效的JSON
  8. https://github.com/eugenp/REST
  9. Spring Web MVC - 验证个别请求参数
  10. 如何使用Curl从终端/命令行POST JSON数据以测试Spring REST?
  11. 如何从Java Servlet返回JSON对象?
  12. 如果REST API返回JSON,它的MIME类型是什么?

1
很棒的教程!继续加油! - Jose
1
@Jose,很高兴你喜欢它,伙计 :) - AmirHd
设置部分存在一个小不一致性。您第一次使用的是MappingJacksonHttpMessageConverter(它是Jackson 1.*),而第二次使用的是MappingJackson2HttpMessageConverter(它兼容jackson 2.*)。 - Ilias Bartolini
1
@IliasBartolini,我在XML设置下面的注释中提到:您可以拥有多个消息转换器。在这里,我创建了一个普通的JSON和一个JSON 2消息转换器。XML文件中使用了Ref和普通bean格式(个人更喜欢ref标签,因为它更整洁)。 - AmirHd
我应该把“handlerAdapter”放在哪里?我会把它放在mvc:annotation-driven中吗? - KyelJmD
@KyelJmD,handlerAdapter bean 与其他 bean 一起位于 MVC Dispatcher XML 文件中。这是您拥有视图解析器 bean 并导入应用程序上下文 XML 的位置。 - AmirHd
2个回答

13

CURL的Post调用

curl -i -H "Content-Type: application/json" -X POST -d '{"id":100,"username":"JohnBlog","name":"John","lastName":"Blog","email":"JohnBlog@user.com"}' http://localhost:8080/[YOURWEBAPP]/api/user/add

不同的错误场景:

在这里,我将探讨在您进行curl调用后可能遇到的不同错误以及可能发生了什么。

场景一:

HTTP/1.1 404 Not Found
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 949
Date: Tue, 04 Jun 2013 02:59:35 GMT

这意味着你提供的URL中没有REST API。
可能是你的请求中有拼写错误(相信我,这种情况很常见)!
也有可能是你的Spring配置不正确。如果是这种情况,需要进一步深入挖掘实际出了什么问题,但我已经提供了一些初始操作,需要在开始更复杂的调查之前执行。
在确保一切都完美无误且配置和URL没有问题之后: - 运行maven clean。 - 卸载你的Web应用程序或直接删除它。 - 重新部署Web应用程序。 - 确保在你的maven/gradle中只使用一个版本的Spring。

场景2:

HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 968
Date: Tue, 04 Jun 2013 03:08:05 GMT
Connection: close

这种情况的唯一原因是您的请求格式不正确。如果您查看详细的curl响应,您应该能够看到“客户端发送的请求在语法上不正确”。
要么您的JSON格式不正确,要么您缺少JAVA对象的必填参数。
请确保以正确的格式提供JSON对象,并提供正确数量的参数。可空属性不是强制性的,但您必须为所有NotNullable属性提供数据。非常重要的是要记住,Spring使用Java反射将您的JSON文件转换为Java对象,这意味着什么?这意味着变量和方法名称是大小写敏感的。如果您的JSON文件发送变量“userName”,那么您的Java对象中相应的变量也必须命名为“userName”。如果您有getter和setter,则它们还必须遵循同样的规则。getUserName和setUserName与我们之前的示例匹配。
场景三:
HTTP/1.1 415 Unsupported Media Type
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 1051
Date: Wed, 24 Aug 2011 08:50:17 GMT

Json媒体类型不受您的Web服务支持。这可能是由于您的注释未指定媒体类型或您在Curl post命令中未指定媒体类型造成的。

检查您的消息转换器是否设置正确,并确保Web服务注释与上面的示例匹配。如果这些都没问题,请确保在Curl post请求中指定content-type。

您的Web服务不支持json媒体类型。

情景N(!):

HTTP/1.1 200 OK 
Server: Apache-Coyote/1.1 
Content-Type: application/json;charset=UTF-8 
Transfer-Encoding: chunked 
Date: Tue, 04 Jun 2013 03:06:16 GMT 

恭喜,用户已经被发送到您的服务器端REST API。如需进一步了解如何设置您的Spring,请查看Spring MVC指南。
相关帖子和问题
如果没有提供以下帖子和问题的所有人(如果我发现有用的相关帖子/问题,此列表将扩展),则此FAQ将不可能。
  1. 什么是正确的 JSON 内容类型?
  2. Spring 3.0 如何使用 Jackson 消息转换器生成 JSON 响应?
  3. 如何使用 Curl 从终端/命令行 POST JSON 数据以测试 Spring REST?
  4. 如何将 JSON 发送到 REST API?
  5. https://github.com/geowarin/spring-mvc-examples
  6. 如何使用 Curl 将 JSON 发送到 PHP?
  7. Spring REST | MappingJacksonHttpMessageConverter 生成无效的 JSON
  8. https://github.com/eugenp/REST
  9. Spring Web MVC - 验证单个请求参数
  10. 如何使用 Curl 从终端/命令行 POST JSON 数据以测试 Spring REST?
  11. 如何从 Java Servlet 返回 JSON 对象?
  12. 如果一个 REST API 返回 JSON,它应该使用哪种 MIME 类型?

1
应该注意到,如果一个bean类有两个或更多的setter方法设置同一属性且没有在可选项上使用@JsonIgnore注释,则无法处理该类。Spring/Jackson会抛出HttpMediaTypeNotSupportedException异常和HTTP状态415 Unsupported Media Type。 示例:
@JsonGetter
public String getStatus() {
    return this.status;
}

@JsonSetter
public void setStatus(String status) {
    this.status = status;
}

@JsonIgnore
public void setStatus(StatusEnum status) {
    if (status == null) {
        throw new NullPointerException();
    }

    this.status = status.toString();
}

更新: 在这种情况下,我们还需要指定@JsonGetter@JsonSetter,以免作为返回类型时出现问题。

我刚刚测试了Spring 3.2.2和Jackson 2.2。它可以作为参数(@RequestBody)和/或返回类型(@ResponseBody)使用,效果很好。

更新2:

如果指定了@JsonGetter@JsonSetter,则似乎不需要@JsonIgnore


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接