Saat menggunakan ResponseEntity <T> dan @RestController untuk aplikasi Spring RESTful

163

Saya bekerja dengan Spring Framework 4.0.7, bersama dengan MVC dan Rest

Saya dapat bekerja dalam damai dengan:

  • @Controller
  • ResponseEntity<T>

Sebagai contoh:

@Controller
@RequestMapping("/person")
@Profile("responseentity")
public class PersonRestResponseEntityController {

Dengan metode (hanya untuk membuat)

@RequestMapping(value="/", method=RequestMethod.POST)
public ResponseEntity<Void> createPerson(@RequestBody Person person, UriComponentsBuilder ucb){
    logger.info("PersonRestResponseEntityController  - createPerson");
    if(person==null)
        logger.error("person is null!!!");
    else
        logger.info("{}", person.toString());

    personMapRepository.savePerson(person);
    HttpHeaders headers = new HttpHeaders();
    headers.add("1", "uno");
    //http://localhost:8080/spring-utility/person/1
    headers.setLocation(ucb.path("/person/{id}").buildAndExpand(person.getId()).toUri());

    return new ResponseEntity<>(headers, HttpStatus.CREATED);
}

untuk mengembalikan sesuatu

@RequestMapping(value="/{id}", method=RequestMethod.GET)
public ResponseEntity<Person> getPerson(@PathVariable Integer id){
    logger.info("PersonRestResponseEntityController  - getPerson - id: {}", id);
    Person person = personMapRepository.findPerson(id);
    return new ResponseEntity<>(person, HttpStatus.FOUND);
}

Bekerja dengan baik

Saya dapat melakukan hal yang sama dengan :

  • @RestController(Saya tahu ini sama dengan @Controller+ @ResponseBody)
  • @ResponseStatus

Sebagai contoh:

@RestController
@RequestMapping("/person")
@Profile("restcontroller")
public class PersonRestController {

Dengan metode (hanya untuk membuat)

@RequestMapping(value="/", method=RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void createPerson(@RequestBody Person person, HttpServletRequest request, HttpServletResponse response){
    logger.info("PersonRestController  - createPerson");
    if(person==null)
        logger.error("person is null!!!");
    else
        logger.info("{}", person.toString());

    personMapRepository.savePerson(person);
    response.setHeader("1", "uno");

    //http://localhost:8080/spring-utility/person/1
    response.setHeader("Location", request.getRequestURL().append(person.getId()).toString());
}

untuk mengembalikan sesuatu

@RequestMapping(value="/{id}", method=RequestMethod.GET)
@ResponseStatus(HttpStatus.FOUND)
public Person getPerson(@PathVariable Integer id){
    logger.info("PersonRestController  - getPerson - id: {}", id);
    Person person = personMapRepository.findPerson(id);
    return person;
}

Pertanyaan saya adalah:

  1. ketika karena alasan yang kuat atau skenario tertentu, satu opsi harus digunakan secara wajib di atas yang lain
  2. Jika (1) tidak masalah, pendekatan apa yang disarankan dan mengapa.
Manuel Jordan
sumber

Jawaban:

213

ResponseEntitydimaksudkan untuk mewakili seluruh respons HTTP. Anda dapat mengontrol apa pun yang masuk ke dalamnya: kode status, header, dan tubuh.

@ResponseBodyadalah penanda untuk badan respons HTTP dan @ResponseStatusmenyatakan kode status respons HTTP.

@ResponseStatussangat tidak fleksibel. Itu menandai seluruh metode sehingga Anda harus yakin bahwa metode penangan Anda akan selalu berlaku dengan cara yang sama. Dan Anda masih tidak dapat mengatur header. Anda memerlukan HttpServletResponseatau HttpHeadersparameter.

Pada dasarnya, ResponseEntitymemungkinkan Anda melakukan lebih banyak.

Sotirios Delimanolis
sumber
6
Poin bagus tentang observasi ketiga. Terima kasih ... dan saya memikirkan hal yang sama ResponseEntity, lebih fleksibel. Hanya saya dengan keraguan tentang @RestController. Terima kasih
Manuel Jordan
55

Untuk melengkapi jawaban dari Sotorios Delimanolis.

Memang benar ResponseEntitymemberi Anda lebih banyak fleksibilitas tetapi dalam kebanyakan kasus Anda tidak akan membutuhkannya dan Anda akan berakhir dengan ini ResponseEntitydi mana - mana di controller Anda sehingga membuatnya sulit untuk dibaca dan dipahami.

Jika Anda ingin menangani kasus-kasus khusus seperti kesalahan (Tidak Ditemukan, Konflik, dll.), Anda dapat menambahkannya HandlerExceptionResolverke konfigurasi Spring Anda. Jadi dalam kode Anda, Anda cukup melempar pengecualian khusus ( NotFoundExceptionmisalnya) dan memutuskan apa yang harus dilakukan di Handler Anda (mengatur status HTTP ke 404), membuat kode Pengontrol lebih jelas.

Mat
sumber
5
Sudut pandang Anda valid bekerja dengan (@) ExceptionHandler. Intinya adalah: jika Anda ingin semua ditangani dalam satu metode (Coba / Tangkap) HttpEntity cocok, jika Anda ingin menggunakan kembali penanganan pengecualian (@) ExceptionHandler untuk banyak (@) Pemetaan Permintaan cocok. Saya suka HttpEntity karena saya dapat bekerja dengan HttpHeaders juga.
Manuel Jordan
46

Menurut dokumentasi resmi: Membuat Pengendali REST dengan anotasi @RestController

@RestController adalah anotasi stereotip yang menggabungkan @ResponseBody dan @Controller. Lebih dari itu, ini memberi lebih banyak makna bagi Pengontrol Anda dan juga dapat membawa semantik tambahan dalam rilis kerangka masa depan.

Tampaknya itu yang terbaik untuk digunakan @RestControlleruntuk kejelasan, tetapi Anda juga dapat menggabungkannya dengan ResponseEntityuntuk fleksibilitas ketika diperlukan ( Menurut tutorial resmi dan kode di sini dan pertanyaan saya untuk mengonfirmasi itu ).

Sebagai contoh:

@RestController
public class MyController {

    @GetMapping(path = "/test")
    @ResponseStatus(HttpStatus.OK)
    public User test() {
        User user = new User();
        user.setName("Name 1");

        return user;
    }

}

sama dengan:

@RestController
public class MyController {

    @GetMapping(path = "/test")
    public ResponseEntity<User> test() {
        User user = new User();
        user.setName("Name 1");

        HttpHeaders responseHeaders = new HttpHeaders();
        // ...
        return new ResponseEntity<>(user, responseHeaders, HttpStatus.OK);
    }

}

Dengan cara ini, Anda ResponseEntityhanya dapat mendefinisikan bila diperlukan.

Memperbarui

Anda bisa menggunakan ini:

    return ResponseEntity.ok().headers(responseHeaders).body(user);
Danail
sumber
Bagaimana jika kita telah menambahkan @ResponseStatus (HttpStatus.OK) pada metode, tetapi metode mengembalikan mengembalikan ResponseEntity baru <> (pengguna, responseHeaders, HttpStatus.NOT_FOUND); Saya hanya berpikir apakah @ResponseStatus akan mengubah kode respons lebih lanjut.
Pratapi Hemant Patel
4
@Hantant tampaknya @ResponseStatus(HttpStatus.OK)diabaikan saat Anda kembali ResponseEntity<>(user, responseHeaders, HttpStatus.NOT_FOUND). Respons HTTP adalah404
Danail
Dari JavaDocs of the ResponseStatus. Kode status diterapkan pada respons HTTP ketika metode handler dipanggil dan mengabaikan informasi status yang ditetapkan dengan cara lain, seperti {@code ResponseEntity} atau {@code "redirect:"}.
vzhemevko
14

API REST yang tepat harus memiliki komponen di bawah sebagai respons

  1. Kode status
  2. Respon Tubuh
  3. Lokasi ke sumber daya yang diubah (misalnya, jika sumber daya dibuat, klien akan tertarik untuk mengetahui url lokasi itu)

Tujuan utama ResponseEntity adalah untuk menyediakan opsi 3, opsi lainnya dapat dicapai tanpa ResponseEntity.

Jadi jika Anda ingin memberikan lokasi sumber daya maka menggunakan ResponseEntity akan lebih baik kalau tidak bisa dihindari.

Pertimbangkan contoh di mana API dimodifikasi untuk menyediakan semua opsi yang disebutkan

// Step 1 - Without any options provided
@RequestMapping(value="/{id}", method=RequestMethod.GET)
public @ResponseBody Spittle spittleById(@PathVariable long id) {
  return spittleRepository.findOne(id);
}

// Step 2- We need to handle exception scenarios, as step 1 only caters happy path.
@ExceptionHandler(SpittleNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Error spittleNotFound(SpittleNotFoundException e) {
  long spittleId = e.getSpittleId();
  return new Error(4, "Spittle [" + spittleId + "] not found");
}

// Step 3 - Now we will alter the service method, **if you want to provide location**
@RequestMapping(
    method=RequestMethod.POST
    consumes="application/json")
public ResponseEntity<Spittle> saveSpittle(
    @RequestBody Spittle spittle,
    UriComponentsBuilder ucb) {

  Spittle spittle = spittleRepository.save(spittle);
  HttpHeaders headers = new HttpHeaders();
  URI locationUri =
  ucb.path("/spittles/")
      .path(String.valueOf(spittle.getId()))
      .build()
      .toUri();
  headers.setLocation(locationUri);
  ResponseEntity<Spittle> responseEntity =
      new ResponseEntity<Spittle>(
          spittle, headers, HttpStatus.CREATED)
  return responseEntity;
}

// Step4 - If you are not interested to provide the url location, you can omit ResponseEntity and go with
@RequestMapping(
    method=RequestMethod.POST
    consumes="application/json")
@ResponseStatus(HttpStatus.CREATED)
public Spittle saveSpittle(@RequestBody Spittle spittle) {
  return spittleRepository.save(spittle);
}

Sumber - Musim Semi Beraksi

Gautam Tadigoppula
sumber