@RequestBody 不适用于 Rest 服务

Posted

技术标签:

【中文标题】@RequestBody 不适用于 Rest 服务【英文标题】:@RequestBody not working on Rest service 【发布时间】:2016-06-17 03:50:11 【问题描述】:

我正在使用 Spring 使用 AngularJS 和 WildFly 开发一个 Web 应用程序。

我的问题是我快疯了,因为注释 @requestBody 似乎工作错误。

这是我的服务:

@ResponseBody
@RequestMapping(value = "/keyuser", method = RequestMethod.POST,
  consumes = "application/json")
public KeyProfileUserSummary updateEmployee(@RequestBody KeyProfileUserSummary keyUser) 
return null;

这是我的对象 KeyProfileUserSummary 的成员:

private Integer id;
private String login;
private String password;
private String firstname;
private String lastname;
private UserRole userRole;

我不知道发生了什么,但我已经使用其他类型的对象测试了此服务并且它运行良好,但是在定义 KeyProfileUserSummary 时它不起作用,我收到 ERROR 400 BAD REQUEST。我已经测试将@RequestBody 设置为“Object”,所以至少我可以看到即将发生的事情,并且从我的前端,我得到以下信息:

id=3, login=aa, password=a, firstname=Martin, lastname=Müller, userRole=ROLE_USER 

UserRole 是一个枚举。重要的是要明确 KeyProfileUserSummary 只是 KeyProfileUser 的摘要版本,但是由于我在响应中得到的所有链接元素,我决定发送这个更轻的类。使用 KeyProfileUser 进行的测试效果很好,我在 Angular 端获得了 JSON 对象并可以将其发回。

在 Angular 方面,我没有对对象做任何事情。只需在列表中接收它,按下编辑按钮时只需将列表中的元素发送回来。这是我发送它的方式:

res = $http.post("url.../keyuser", user);

问题是我使用 KeyProfileUser 可以完美运行所有内容,但是由于数据库可能变得非常庞大并且参考非常多,我决定切换到这个更轻的类,但现在我只收到这个 ERROR 400 BAD REQUEST ...我要上吊了:P

感谢您的帮助!

【问题讨论】:

从服务器获取的错误是什么,我的意思是堆栈跟踪类似于 MessageConverter 问题? 【参考方案1】:

好的,我终于找到了解决方案。

在我的 KeyProfileUserSummary 中,我只有一个构造函数,它采用 KeyProfileUser 并将属性设置为摘要版本:

public KeyProfileUserSummary(KeyProfileUser keyProfileUser) 
  this.id = keyProfileUser.getId();
  this.login = keyProfileUser.getLogin();
  this.password = keyProfileUser.getPassword();
  this.firstname = keyProfileUser.getPerson().getFirstname();
  this.lastname = keyProfileUser.getPerson().getLastname();
  this.userRole = keyProfileUser.getUserRole();

显然,在调度程序 servlet 的第 993 行设置一个断点(感谢@Clemens Eberwein 的提示)我意识到从 JSON 对象解析时,Jackson 解析器 需要一个空的构造函数!所以添加它解决了它并且完美地工作。

注意:对于 KeyProfileUser,它工作得很好,因为我们有注解 @Entity 用于休眠,因此会自动创建空构造函数。

【讨论】:

非常感谢!通过为暴露的 dto 中的字段类添加默认构造函数来帮助我【参考方案2】:

试试这个..可能对你有用..

$http(
    method: 'POST',
    url: 'http://localhost:8080/keyuser',
    data: user,
    headers: 
        'Content-Type': 'application/json',
        'Accept': 'application/json'
    ).then(function(result) 
           console.log(result);
       , function(error) 
           console.log(error);
       );

【讨论】:

感谢您的提示,但显然它也是如此。尝试使用 DELETE 而不是 POST 方法删除用户时,我遇到了同样的问题,实际上我有一些与您的解决方案非常相似的东西,但我只是收到相同的错误 400 ...而且控制台没有记录任何有用的信息,只是带有错误 400 的 html ......我想我只会发送我想要更新的值作为参数,因为这需要很多时间......【参考方案3】:

如果我猜的话,杰克逊在反序列化/序列化您的对象方面失败了。这是我制作的一个实用程序:

import java.io.IOException;
import java.nio.charset.Charset;

import org.springframework.http.MediaType;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class SerializeDeserializeUtil 

    public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(
            MediaType.APPLICATION_JSON.getType(),
            MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));

    public static byte[] convertObjectToJsonBytes(Object object)
            throws IOException 
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(Include.NON_NULL);

        return mapper.writeValueAsBytes(object);

    

    public static <T> T deserializeObject(String jsonRepresentation,
            Class<T> clazz) throws JsonParseException, JsonMappingException,
            IOException 

        ObjectMapper mapper = new ObjectMapper();

        Object obj = mapper.readValue(jsonRepresentation.getBytes(), clazz);

        return clazz.cast(obj);
    


    @SuppressWarnings( "unchecked", "rawtypes" )
    public static byte[] convertObjectToJsonBytesWithCustomSerializer(
            Object object, JsonSerializer serializer, Class clazz)
            throws IOException 

        ObjectMapper mapper = new ObjectMapper();

        SimpleModule sm = new SimpleModule();
        sm.addSerializer(clazz, serializer);

        mapper.registerModule(sm);
        mapper.setSerializationInclusion(Include.NON_NULL);

        return mapper.writeValueAsBytes(object);

    


尝试创建一个测试来序列化和反序列化对象。创建一个 KeyProfileUserSummary 对象并尝试反序列化/序列化,看看杰克逊是否抱怨。

更简单的方法是启用 DEBUG 日志记录并检查日志文件,默认情况下您不会看到此类错误

希望对你有帮助。

【讨论】:

如何在 Wildfly 上启用 DEBUG?以前从未这样做过...但是看到错误可能会有所帮助。 你在用spring-boot吗? 我不知道那是什么,我只使用带有wildfly服务器的eclipse。 看看这个mkyong.com/spring-mvc/spring-mvc-log4j-integration-example。但是,要解决您的上述问题,您可以尝试手动序列化/反序列化您的对象。只是为了检查是否按预期工作 但问题是我们的服务对每个类都运行良好,我不想再增加一层来反序列化对象......这个错误肯定是愚蠢的......我们有具有大量引用的复杂类,并且在这个具有 6 个属性的非常简单的类上,它不起作用:(【参考方案4】:

如果您为“org.springframework.web”添加调试日志记录,org.springframework.web.servlet.DispatcherServlet 应该会为您提供导致“400 Bad Request”错误的详细信息。

Wildfly 日志配置详情见here

根据您的 KeyProfileUserSummary 类,我猜问题是 UserRole 对象,在上面的示例中它只是 userRole=ROLE_USER。因为它是一个对象,所以它应该用花括号括起来,并且必须设置属性名称。例如像

userRole =  name = "ROLE_USER"

如果是枚举,this 的回答可能会有所帮助

【讨论】:

对不起,我的无知,但我在 Spring 上添加调试的经验并不多,我阅读了一些文档但我不知道,没有 application.properties,没有 log4j 文件或类似的东西。关于枚举的事情,这不是问题,因为使用 KeyProfileUser 它可以工作并且枚举以相同的方式发送。明天我会继续寻找解决方案,但感谢您的提示,在日志中查看其他内容会非常有用。 在遇到同样问题后帮助我让控制器正常工作的方法是简单地添加一个 get 方法并查看控制器“认为”什么是有效对象。通过这种方式,我发现属性问题s 缺少“s”;) 如果您能够在 DispatcherServlet 中设置断点,则无需将日志级别设置为 DEBUG 即可在内部获取异常。对我来说(春季 3.2.9)它是 DispatcherServlet private void processDispatchResult 第 993 行。只需在调度程序 servlet 中搜索“logger.isDebugEnabled()”。 我找到了解决办法!!将调试器设置为 DEBUG 并没有显示任何不同(也许我做错了什么),但是在那里设置断点让我看到了问题!非常感谢,我真的花了很多时间才找到这个!

以上是关于@RequestBody 不适用于 Rest 服务的主要内容,如果未能解决你的问题,请参考以下文章

Keycloak 注销不适用于公开 REST 服务的“仅承载”应用程序

REST 服务的身份验证不适用于 jBPM(KIE 服务器和业务中心)和 Keycloak

Spring Rest Service 不适用于 XML 响应

带有 Spring Security 核心和 CORS 插件的 grails REST API 不适用于 OPTIONS http 方法请求

Java Spring REST API CORS 不适用于通过 jQuery 和 Chrome 的 DELETE 请求

Spring REST多个@RequestBody参数,可能吗?