เมื่อใช้ ResponseEntity <T> และ @RestController สำหรับแอปพลิเคชัน Spring RESTful


163

ฉันทำงานกับ Spring Framework 4.0.7 พร้อมด้วย MVC และ Rest

ฉันสามารถทำงานด้วยความสงบสุขด้วย:

  • @Controller
  • ResponseEntity<T>

ตัวอย่างเช่น:

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

ด้วยวิธีการ (เพียงเพื่อสร้าง)

@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);
}

เพื่อส่งคืนบางสิ่ง

@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);
}

ทำงานได้ดี

ฉันสามารถทำเช่นเดียวกันกับ :

  • @RestController(ฉันรู้ว่ามันเหมือนกับ@Controller+ @ResponseBody)
  • @ResponseStatus

ตัวอย่างเช่น:

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

ด้วยวิธีการ (เพียงเพื่อสร้าง)

@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());
}

เพื่อส่งคืนบางสิ่ง

@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;
}

คำถามของฉันคือ:

  1. เมื่อมีเหตุผลที่ชัดเจนหรือสถานการณ์ที่เฉพาะเจาะจงตัวเลือกหนึ่งจะต้องใช้ mandatorily มากกว่าตัวเลือกอื่น
  2. ถ้า (1) ไม่สำคัญก็จะแนะนำวิธีการอะไรและทำไม

คำตอบ:


213

ResponseEntityมีวัตถุประสงค์เพื่อแสดงการตอบสนอง HTTP ทั้งหมด คุณสามารถควบคุมทุกสิ่งที่เกี่ยวข้อง: รหัสสถานะส่วนหัวและเนื้อหา

@ResponseBodyเป็นตัวทำเครื่องหมายสำหรับเนื้อหาการตอบสนอง HTTP และ@ResponseStatusประกาศรหัสสถานะของการตอบสนอง HTTP

@ResponseStatusไม่ยืดหยุ่นมาก มันทำเครื่องหมายวิธีการทั้งหมดดังนั้นคุณต้องแน่ใจว่าวิธีการจัดการของคุณจะทำงานในลักษณะเดียวกัน และคุณยังคงไม่สามารถตั้งค่าส่วนหัว คุณต้องการHttpServletResponseหรือHttpHeadersพารามิเตอร์

โดยทั่วไปResponseEntityช่วยให้คุณทำมากขึ้น


6
จุดที่ดีเกี่ยวกับการสังเกตที่สาม ขอบคุณ…และฉันคิดเหมือนกันResponseEntityว่ามันยืดหยุ่นกว่า @RestControllerเพียงแค่ผมกับข้อสงสัยเกี่ยวกับ ขอบคุณ
Manuel Jordan

55

เพื่อให้คำตอบจาก Sotorios Delimanolis

มันเป็นความจริงที่ResponseEntityให้ความยืดหยุ่นมากขึ้น แต่ในกรณีส่วนใหญ่คุณไม่จำเป็นต้องใช้มันและคุณจะพบสิ่งเหล่านี้ได้ResponseEntityทุกที่ในคอนโทรลเลอร์ของคุณซึ่งทำให้อ่านและเข้าใจได้ยาก

หากคุณต้องการจัดการกรณีพิเศษเช่นข้อผิดพลาด (ไม่พบความขัดแย้ง ฯลฯ ) คุณสามารถเพิ่มการHandlerExceptionResolverกำหนดค่าสปริงของคุณ ดังนั้นในรหัสของคุณคุณเพียงแค่โยนข้อยกเว้นเฉพาะ ( NotFoundExceptionเช่น) และตัดสินใจว่าจะทำอย่างไรใน Handler ของคุณ (การตั้งค่าสถานะ HTTP เป็น 404) ทำให้รหัสคอนโทรลเลอร์ชัดเจนขึ้น


5
มุมมองของคุณใช้ได้กับ (@) ExceptionHandler ประเด็นคือ: ถ้าคุณต้องการจัดการทั้งหมดในหนึ่งวิธี (ลอง / จับ) HttpEntity เหมาะสมถ้าคุณต้องการใช้การจัดการข้อยกเว้น (@) ExceptionHandler สำหรับหลาย ๆ (@) RequestMapping ที่เหมาะสม ฉันชอบ HttpEntity เพราะฉันสามารถทำงานกับ HttpHeaders ได้เช่นกัน
Manuel Jordan

46

ตามเอกสารประกอบอย่างเป็นทางการ: การสร้าง REST Controllers ด้วยคำอธิบายประกอบ @RestController

@RestController เป็นคำอธิบายประกอบแบบตายตัวที่รวม @ResponseBody และ @Controller ยิ่งไปกว่านั้นมันให้ความหมายกับตัวควบคุมของคุณมากกว่าและอาจมีความหมายเพิ่มเติมในการเผยแพร่เฟรมเวิร์กในอนาคต

ดูเหมือนว่าจะเป็นการดีที่สุดที่จะใช้@RestControllerเพื่อความชัดเจน แต่คุณสามารถรวมเข้ากับResponseEntityความยืดหยุ่นเมื่อจำเป็น ( ตามการสอนอย่างเป็นทางการและรหัสที่นี่และคำถามของฉันเพื่อยืนยันว่า )

ตัวอย่างเช่น:

@RestController
public class MyController {

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

        return user;
    }

}

เหมือนกับ:

@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);
    }

}

ด้วยวิธีนี้คุณสามารถกำหนดได้ResponseEntityเมื่อจำเป็นเท่านั้น

ปรับปรุง

คุณสามารถใช้สิ่งนี้:

    return ResponseEntity.ok().headers(responseHeaders).body(user);

จะเกิดอะไรขึ้นถ้าเราได้เพิ่ม @ResponseStatus (HttpStatus.OK) ในวิธีการ แต่วิธีการส่งกลับผลตอบแทนใหม่ ResponseEntity <> (ผู้ใช้ responseHeaders, HttpStatus.NOT_FOUND); ฉันแค่คิดว่า @ResponseStatus จะแก้ไขรหัสการตอบสนองเพิ่มเติมหรือไม่
Pratapi Hemant Patel

4
@Hemant ดูเหมือนว่าจะถูกละเว้นเมื่อคุณกลับมา@ResponseStatus(HttpStatus.OK) ResponseEntity<>(user, responseHeaders, HttpStatus.NOT_FOUND)การตอบสนองของ HTTP คือ404
Danail

จาก JavaDocs ของ ResponseStatus รหัสสถานะจะใช้กับการตอบสนอง HTTP เมื่อมีการเรียกใช้เมธอดตัวจัดการและแทนที่ข้อมูลสถานะที่กำหนดโดยวิธีการอื่นเช่น {@code ResponseEntity} หรือ {@code "redirect:"}
vzhemevko

14

REST API ที่เหมาะสมควรมีองค์ประกอบด้านล่างในการตอบสนอง

  1. รหัสสถานะ
  2. Response ร่างกาย
  3. ตำแหน่งของทรัพยากรที่มีการเปลี่ยนแปลง (ตัวอย่างเช่นหากมีการสร้างทรัพยากรลูกค้าจะสนใจทราบ URL ของสถานที่นั้น)

วัตถุประสงค์หลักของ ResponseEntity คือเพื่อให้ตัวเลือก 3 ตัวเลือกพักผ่อนสามารถทำได้โดยไม่ต้อง ResponseEntity

ดังนั้นหากคุณต้องการระบุตำแหน่งของทรัพยากรการใช้ ResponseEntity จะดีกว่าหากเป็นอย่างอื่นก็สามารถหลีกเลี่ยงได้

พิจารณาตัวอย่างที่ปรับเปลี่ยน API เพื่อให้ตัวเลือกทั้งหมดที่กล่าวถึง

// 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);
}

ที่มา - การกระทำในฤดูใบไม้ผลิ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.