【问题标题】:Post method not reading json, only String in Spring Boot apiPost 方法不读取 json,只有 Spring Boot api 中的 String
【发布时间】:2022-01-13 18:03:11
【问题描述】:

我正在开发一个简单的 CRUD api,但我的 post 方法不起作用,每次发送 json 格式的数据时都会引发此异常

{
    "title": "Cannot construct instance of `academy.devdojo.springboot2.requests.AnimePostRequestBody` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (PushbackInputStream); line: 2, column: 5]",
    "status": 400,
    "details": "JSON parse error: Cannot construct instance of `academy.devdojo.springboot2.requests.AnimePostRequestBody` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `academy.devdojo.springboot2.requests.AnimePostRequestBody` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (PushbackInputStream); line: 2, column: 5]",
    "developerMessage": "org.springframework.http.converter.HttpMessageNotReadableException",
    "timestamp": "2022-01-13T14:45:34.9138659"
}

它只在发送一个简单的字符串比如“要保存的名字”时才有效,而不是

{
  "name" : "some name to be saved"
}

这是我的课

实体

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Builder
public class Anime {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotEmpty(message= "anime name cannot be empty")
    private String name;

}

回购

public interface AnimeRepository extends JpaRepository<Anime, Long> {
    List<Anime> findByName(String name);
}

服务

@Service
@RequiredArgsConstructor
public class AnimeService {

    private final AnimeRepository animeRepository;

    public Page<Anime> listAll(Pageable pageable) {

        return animeRepository.findAll(pageable);
    }

    public List<Anime> findByName(String name) {
        List<Anime> byName = animeRepository.findByName(name);
        if (byName.isEmpty()){
            throw new BadRequestException("Anime not Found");
        }
        return byName;
    }

    public Anime findByIdOrThrowBadRequestException(long id) {
        return animeRepository.findById(id)
                .orElseThrow(() -> new BadRequestException("Anime not Found"));
    }


    public Anime save(AnimePostRequestBody animePostRequestBody) {

        Anime anime = AnimeMapper.INSTANCE.toAnime(animePostRequestBody);

        return animeRepository.save(anime);
    }

    public void delete(long id) {

        animeRepository.delete(findByIdOrThrowBadRequestException(id));
    }

    public void replace(AnimePutRequestBody animePutRequestBody) {

        Anime savedAnime  = findByIdOrThrowBadRequestException(animePutRequestBody.getId());

        Anime anime = AnimeMapper.INSTANCE.toAnime(animePutRequestBody);

        anime.setId(savedAnime.getId());

        animeRepository.save(anime);
    }

    public List<Anime> listAllNonPageable() {
        return animeRepository.findAll();
    }
}

休息控制器

@RestController
@Api(tags = "Atividades Economicas")
@RequestMapping("animes")
@Log4j2
@RequiredArgsConstructor
public class AnimeController {
    private final DateUtil dateUtil;
    private final AnimeService animeService;

    @ApiOperation(value = "Listar todos")
    @GetMapping
    public ResponseEntity<Page<Anime>> list(Pageable pageable) {
        //log.info(dateUtil.formatLocalDateTimeToDatabaseStyle(LocalDateTime.now()));
        return ResponseEntity.ok(animeService.listAll(pageable));
    }

    @GetMapping(path = "/all")
    public ResponseEntity<List<Anime>> listAll() {
        //log.info(dateUtil.formatLocalDateTimeToDatabaseStyle(LocalDateTime.now()));
        return ResponseEntity.ok(animeService.listAllNonPageable());
    }
    @ApiOperation(value = "Listar por ID")
    @GetMapping(path = "/{id}")
    public ResponseEntity<Anime> findById(@PathVariable long id) {
        return ResponseEntity.ok(animeService.findByIdOrThrowBadRequestException(id));
    }

    @ApiOperation(value = "Encontrar pelo nome")
    @GetMapping(path = "/find")
    public ResponseEntity<List<Anime>> findByName(@RequestParam String name) {
        return ResponseEntity.ok(animeService.findByName(name));
    }


    @PostMapping
    public ResponseEntity<Anime> save(@RequestBody @Valid AnimePostRequestBody animePostRequestBody) {
        return new ResponseEntity<>(animeService.save(animePostRequestBody), HttpStatus.CREATED);
    }


    @DeleteMapping(path = "/{id}")
    public ResponseEntity<Void> delete(@PathVariable long id) {
        animeService.delete(id);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @PutMapping
    public ResponseEntity<Void> replace(@RequestBody AnimePutRequestBody animePutRequestBody) {
        animeService.replace(animePutRequestBody);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
}

DTO 的

@Data
@Builder
public class AnimePostRequestBody {

   @NotEmpty(message= "anime name cannot be empty")
    private String name;

}

@Data
@Builder
public class AnimePutRequestBody {
    private Long id;
    private String name;
}

映射器

@Mapper(componentModel = "spring")
public abstract class AnimeMapper {

    public static  final AnimeMapper INSTANCE = Mappers.getMapper(AnimeMapper.class);

    public abstract Anime toAnime(AnimePostRequestBody animePostRequestBody);
    public abstract Anime toAnime(AnimePutRequestBody animePutRequestBody);
}

附言尽管它们非常相似,但 put 方法运行良好。

【问题讨论】:

    标签: java json spring-boot post dto


    【解决方案1】:

    @Data 是一个方便的快捷注解,将@ToString、@EqualsAndHashCode、@Getter/@Setter 和@RequiredArgsConstructor 的功能捆绑在一起

    需要默认构造函数。添加 lombok 的 @NoArgsConstructor 注解。

    【讨论】:

      【解决方案2】:

      除了 Model 类,DTO 类还应该包含 @AllArgsConstructor 和 @NoArgsConstructor。要将 RequestBody 值映射到 DTO,它需要构造函数,而 @Data 不提供 @AllArgsConstructor 和 @NoArgsConstructor 构造函数。

      【讨论】:

        【解决方案3】:

        //实体

            @Data
            @Entity
            @Table(name = "Anime")
            @Builder
            public class Anime {
        
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            private Long id;
        
            @NotEmpty(message= "anime name cannot be empty")
            private String name;
        
        }
        

        //回购

        @Repository
        public interface AnimeRepository extends JpaRepository<Anime, Long> {
        }
        

        //DTO

        @Getter
        public class AnimePostRequestBody {
        
            @NotEmpty(message = "anime name cannot by empty")
            private String name;
        }
        

        //控制器

        @RestController
        @RequestMapping("animes")
        public class AnimeController {
        
            @Autowired
            private AnimeService animeService;
        
            @PostMapping
            public ResponseEntity<Anime> save(@RequestBody @Valid AnimePostRequestBody animePostRequestBody) {
                return new ResponseEntity<>(animeService.save(animePostRequestBody), HttpStatus.CREATED);
            }
        
        
        }
        

        //服务

        @Service
        public class AnimeService {
        
            @Autowired
            private AnimeRepository animeRepository;
        
            public Anime save(AnimePostRequestBody animePostRequestBody) {
                Anime anime = Anime.builder()
                        .name(animePostRequestBody.getName())
                        .build();
                return animeRepository.save(anime);
            }
        }
        

        输出

        { “身份证”:4, “名称”:“火影忍者” }

        【讨论】:

          【解决方案4】:

          替换这一行

           public ResponseEntity<Anime> save(@RequestBody @Valid AnimePostRequestBody animePostRequestBody){..
          

          用这个:

           public ResponseEntity<Anime> save(@Valid @RequestBody AnimePostRequestBody animePostRequestBody)
          

          【讨论】:

            猜你喜欢
            • 2016-02-15
            • 2017-11-02
            • 1970-01-01
            • 1970-01-01
            • 2015-12-05
            • 1970-01-01
            • 2019-08-23
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多