Spring Boot Converter 在 REST 调用中不起作用

问题描述 投票:0回答:1

我正在尝试使用 Converter 将来自 POST 请求的 String 输入转换为 CountryDTO 对象。我已经创建了 CountryConverter 类并实现了来自 Spring 的 Converter 接口,我还将转换器添加到了 WebConfig 类中的 FormatterRegistry 中。

它适用于普通的 @Controller 类方法,但是,如果我使用一些 HTML 模板和表单,它在执行任何 @RestController 类方法之前不起作用。

这是我在控制台中收到的消息,我了解此消息的原因。由于我的转换器不起作用,应用程序尝试将 String 反序列化为 CountryDTO 对象,但它失败了,因为它无法以 String“1”为例,并将其反序列化为整个 CountryDTO 对象。

控制台消息:

已解决[org.springframework.http.converter.HttpMessageNotReadableException:JSON解析错误:无法构造

com.cydeo.dto.CountryDTO

的实例(尽管至少存在一个创建者):没有字符串参数构造函数/工厂方法可以从字符串值(“1”)反序列化);嵌套异常是 com.fasterxml.jackson.databind.exc.MismatchedInputException:无法构造 
com.cydeo.dto.CountryDTO
 的实例(尽管至少存在一个 Creator):没有字符串参数构造函数/工厂方法可以从字符串值('1')反序列化[来源:(org.springframework.util.StreamUtils$NonClosingInputStream);行:5,列:16](通过参考链:com.cydeo.dto.UserDTO[“country”])]

我正在使用

Spring Boot版本2.7.7

这是我的

JSON 请求正文:

{ "username": "MikeS", "password": "Abc1", "country": "1" }
这是我的代码:

我的国家转换器课程:

import com.cydeo.dto.CountryDTO; import com.cydeo.service.CountryService; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; @Component public class CountryConverter implements Converter<String, CountryDTO> { private final CountryService countryService; public CountryConverter(CountryService countryService) { this.countryService = countryService; } @Override public CountryDTO convert(String source) { if (source.isEmpty()) { return null; } try { return countryService.findById(Long.parseLong(source)); } catch (Exception e) { e.printStackTrace(); return null; } } }

我的WebConfig类:

import com.cydeo.converter.CountryConverter; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer { private final CountryConverter countryConverter; public WebConfig(CountryConverter countryConverter) { this.countryConverter = countryConverter; } @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(countryConverter); } }

我的 UserDTO 课程:

import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import javax.validation.constraints.*; @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class UserDTO { @JsonIgnore private Long id; @NotBlank(message = "Username is required") @Size(min = 3, max = 16, message = "Username length should be min 2, max 16") private String username; @NotBlank(message = "Password is required") @Pattern(regexp = "(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{4,}", message = "The password should be at least 4 characters long and include at least 1 capital letter, 1 small letter and 1 digit") private String password; @NotNull(message = "Country is required") private CountryDTO country; }

我的国家DTO课程:

import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class CountryDTO { @JsonIgnore private Long id; private String countryName; }

我的 UserController 类:

import com.cydeo.dto.ResponseWrapper; import com.cydeo.dto.UserDTO; import com.cydeo.service.UserService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.*; @RestController @RequestMapping("/api/user") public class UserRestController { private final UserService userService; public UserRestController(UserService userService) { this.userService = userService; } @PostMapping("/create") public ResponseEntity<ResponseWrapper> createUser(@Valid @RequestBody UserDTO userDTO, BindingResult bindingResult) throws Exception { if (bindingResult.hasErrors()) { List<FieldError> fieldErrors = bindingResult.getFieldErrors(); Map<String, Map<String, String>> response = new HashMap<>(); for (FieldError fieldError : fieldErrors) { if (response.containsKey(fieldError.getField())) { response.get(fieldError.getField()).put(fieldError.getCode(), fieldError.getDefaultMessage()); } else { response.put(fieldError.getField(), new HashMap<>()); response.get(fieldError.getField()).put(fieldError.getCode(), fieldError.getDefaultMessage()); } } return ResponseEntity.badRequest().body(new ResponseWrapper(false, "Please check the information.", HttpStatus.BAD_REQUEST, response)); } return ResponseEntity.status(HttpStatus.CREATED).body(new ResponseWrapper("User is created.", userService.create(userDTO), HttpStatus.CREATED)); } }
    
java json spring spring-boot converters
1个回答
0
投票
您实现的转换器接口

org.springframework.core.convert.converter.Converter

主要用于应用层的类型转换,例如服务或控制器层内不同对象类型之间的转换。不过,它并不直接参与HTTP消息转换。

在处理JSON数据时,Spring Boot默认使用Jackson来序列化和反序列化请求和响应主体。在这种情况下,Jackson 不会自动利用 Spring Converter 接口进行 JSON 反序列化。

您有几个选择:

    为 Jackson 实现自定义解串器
  1. 修改UserDTO类
您现有的 CountryConverter 对于 Web 表单提交或请求参数转换等场景完全有效。然而,当涉及到 @RestController 中的 JSON 数据处理时,需要与 Jackson 集成才能适用于 JSON 反序列化。

示例:

public class CountryDTODeserializer extends JsonDeserializer<CountryDTO> { private final CountryService countryService; public CountryDTODeserializer(CountryService countryService) { this.countryService = countryService; } @Override public CountryDTO deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); Long id = node.asLong(); // Something like this try { return countryService.findById(id); } catch (Exception e) { throw new RuntimeException("Error during deserialization", e); } } }
另外不要忘记添加以下注释:

@JsonDeserialize(using = CountryDTODeserializer.class) @NotNull(message = "Country is required") private CountryDTO country;
    
© www.soinside.com 2019 - 2024. All rights reserved.