通过Spring进行HTTP POST调用添加新对象的最佳方法是什么?

huangapple go评论65阅读模式
英文:

What's the best way to add a new Object using the HTTP POST call through Spring?

问题

以下是翻译好的部分:

我正在学习Spring Framework,我在使用Spring的Rest服务方面遇到了困难,特别是针对应该向数据库添加新对象的POST调用。
我在网上看到了许多不同的实现,但我不知道如何选择最佳实践。

以电影类为例:

@Entity
public class Film {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private long id;

  private String title;

  private String description;

  // 构造函数、Getter和Setter已省略。
}

假设该存储库扩展了JpaRepository<Film,Long>,以下是Controller类:

@RestController
public class FilmController {

  @Autowired
  FilmRepository filmRepository;
 
  // 实现方法 #1
  @PostMapping("/film")
  public Film addNew(@RequestBody Map<String,String> body){
    String title = body.get("title");
    String description = body.get("description");
    return filmRepository.save(new Film(title,description));
  }
  
  // 实现方法 #2
  @PostMapping("/film")
  public Film addNew(String title, String description){
    Film film = new Film(title,description);
    System.out.println(film.getTitle() + " " + film.getDescription());
    return filmRepository.save(film);
  }

  // 实现方法 #3
  @PostMapping("/film")
  public Film addNew(@RequestBody Film newFilm){
    return filmRepository.save(newFilm);
  }
}

为什么有些实现将Map&lt;String, String&gt;作为参数?这是一个映射到键值对的请求体吗?

另外要注意,我成功实现了方法 #2,但第一种和第三种方法给了我一个415 错误:"不支持的媒体类型"(尽管我遵循了官方的Spring教程)中关于REST服务的部分。

我还阅读了关于创建DTO类的内容,在DTO类中,我可以定义属性而不将对象暴露给控制器,如何实现这样的解决方案?

英文:

I'm learning the Spring Framework and I'm struggling with the Rest services with spring, in particular for the POST call that it's supposed to add a new object to the database.
I've seen a lot of different implementations through the web, but I don't know how to pick the best.

Let's take for example a film class:

@Entity
public class Film {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private long id;

  private String title;

  private String description;

  //Constructor, Getter and Setter Omitted.
}

Assuming the repository extends the JpaRepository<Film,Long>, this would be the Controller class:

@RestController
public class FilmController {

  @Autowired
  FilmRepository filmRepository;
 
  //Implementation #1
  @PostMapping(&quot;/film&quot;)
  public Film addNew(@RequestBody Map&lt;String,String&gt; body){
    String title = body.get(&quot;title&quot;);
    String description = body.get(&quot;description&quot;);
    return filmRepository.save(new Film(title,description));
  }
  
  //Implementation #2
  @PostMapping(&quot;/film&quot;)
  public Film addNew(String title, String description){
    Film film = new Film(title,description);
    System.out.println(film.getTitle() + &quot; &quot; + film.getDescription());
    return filmRepository.save(film);
  }

  //Implementation #3
  @PostMapping(&quot;/film&quot;)
  public Film addNew(@RequestBody Film newFilm){
    return filmRepository.save(newFilm);
  }

}

Why some implementations have as parameter a Map&lt;String, String&gt; ? Is that a body mapped to a key/value pair ?

Also bear in mind that I managed to implement correctly just the implementation #2, the first and the third gave me a

415 error:&quot;Unsupported Media Type&quot; org.springframework.web.HttpMediaTypeNotSupportedException: Content type &#39;multipart/form-data;boundary=--------------------------901298977805450214809889;charset=UTF-8&#39; not supported]

(Despite I followed the official Spring tutorial) on REST services.

I also read something about the creation of DTO classes where I can define attributes without exposing the object to the controller, how can be implemented such solution?

答案1

得分: 1

  • 为什么一些实现中的参数是 Map<String, String>?
    一些实现使用 map&lt;key,value&gt;,因为它们需要映射接口提供的属性,比如非重复键值,或者实现映射接口的类,比如 TreeMapLinkedHashMap
  • 关于你的 FilmController 类的实现,我认为对于将你的域发布到数据库中,并不需要使用 map<String,String>,你可以有如下实现:
@RestController
public class FilmController {

  @Autowired
  FilmRepository filmRepository;
 
  @PostMapping("/film")
  public ResponseEntity addNew(@RequestBody Film film){
    return ResponseEntity.ok(filmRepository.save(film));
  }
}
英文:
  • Why some implementations have as parameter a Map<String, String> ?<br/>
    some implementations use map&lt;key,value&gt; because they need the properties that map interface provide such as non-duplicate key value or the classes that implement map interface such as TreeMap and LinkedHashMap.
  • about your implementation of the class FilmController i think its not necessary to use map<String,String> for posting your domain in the data base simply you can have this implementation
@RestController
public class FilmController {

  @Autowired
  FilmRepository filmRepository;
 
  @PostMapping(&quot;/film&quot;)
  public ResponseEntity addNew(@RequestBody Film film){
return ResponseEntity.ok(filmRepository.save(film));


答案2

得分: 1

实施方法3是最佳实践,但您应创建一个轻量级的DTO类(可能是FilmDto),以避免暴露实体的内部结构,请参阅LocalDTO,Martin Fowler

您可以使用ModelMapper将FilmDto映射到Film,并确保两个类中都有适当的getter和setter。如果两个类中的getter和setter具有相同的名称,那么ModelMapper将会顺利进行转换:

public class FilmDto {
    private long id;
    private String title;
    private String description;

    public long getId() {
        return id;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

以及您的控制器:

@RestController
@RequestMapping("/api")
public class FilmController {

    private final FilmRepository filmRepository;
    private ModelMapper modelMapper = new ModelMapper();
    @Autowired
    public FilmController(FilmRepository filmRepository) {
        this.filmRepository = filmRepository;
    }

    //Implementation #3
    @PostMapping("/film")
    public ResponseEntity<FilmDto> addNew(@RequestBody FilmDto  filmDto){
        Film newFilm = modelMapper.map(filmDto, Film.class);
        newFilm = filmRepository.save(newFilm);
        filmDto.setId(newFilm.getId()); //在这里可以使用modelMapper

        return ResponseEntity.ok(filmDto);
    }
}

您可以使用Postman进行测试,通过以下方式传递电影信息:

{
    "title": "some title",
    "description": "some description"
}

请求体应为类型“raw”和“JSON”。

英文:

Implementation 3 is the best practice, but you should create a lightweight DTO class (maybe FilmDto) to avoid exposing the internal structure of your entity, please see LocalDTO, Martin Fowler.

You may use ModelMapper to map FilmDto to Film, and make sure there are proper getters and setters in both classes, if the getters and setters have the same names in both classes, then ModelMapper will do the conversion smoothly:

public class FilmDto {
    private long id;
    private String title;
    private String description;

    public long getId() {
        return id;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

and you controller:

@RestController
@RequestMapping(&quot;/api&quot;)
public class FilmController {

    private final FilmRepository filmRepository;
    private ModelMapper modelMapper = new ModelMapper();
    @Autowired
    public FilmController(FilmRepository filmRepository) {
        this.filmRepository = filmRepository;
    }


    //Implementation #3
    @PostMapping(&quot;/film&quot;)
    public ResponseEntity&lt;FilmDto&gt; addNew(@RequestBody FilmDto  filmDto){
        Film newFilm = modelMapper.map(filmDto, Film.class);
        newFilm = filmRepository.save(newFilm);
        filmDto.setId(newFilm.getId());//you may use modelMapper here

        return ResponseEntity.ok(filmDto);
    }
}

you can test using postman by passing the film as below:

{
    &quot;title&quot;: &quot;some title&quot;,
    &quot;description&quot;: &quot;some description&quot;
}

and the body should be of type "raw", "JSON".

huangapple
  • 本文由 发表于 2020年10月23日 08:37:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/64492355.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定