← Назад к вопросам

В чем разница между ResponseEntity и ResponseBody?

2.0 Middle🔥 161 комментариев
#REST API и микросервисы#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# ResponseEntity vs @ResponseBody в Spring

Это два способа отправить данные клиенту в HTTP ответе. Они решают разные задачи и имеют разные уровни контроля.

@ResponseBody — просто сериализация

// @ResponseBody — сериализует объект в response body
@Controller
public class UserController {
    @GetMapping("/users/{id}")
    @ResponseBody
    public User getUser(@PathVariable Long id) {
        return new User(id, "Alice", "alice@example.com");
        // Spring автоматически сериализует User в JSON
        // HTTP Status: 200 (по умолчанию)
        // Headers: установлены автоматически
    }
    
    @PostMapping("/users")
    @ResponseBody
    public List<User> getAllUsers() {
        return Arrays.asList(
            new User(1, "Alice", "alice@example.com"),
            new User(2, "Bob", "bob@example.com")
        );
        // Возвращает 200 OK с JSON массивом
    }
}

Характеристики:

  • ✅ Простая сериализация объекта в JSON/XML
  • ✅ Компактный синтаксис
  • Нет контроля над HTTP статусом
  • Нет контроля над заголовками
  • Всегда возвращает 200 OK (или ошибку)
  • ❌ Нельзя установить custom headers
  • ❌ Нельзя вернуть другой статус при успехе

ResponseEntity — полный контроль

// ResponseEntity — полный контроль над ответом
@RestController
public class UserController {
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = new User(id, "Alice", "alice@example.com");
        
        return ResponseEntity.ok(user);
        // Эквивалентно:
        // return ResponseEntity.status(HttpStatus.OK).body(user);
    }
    
    @PostMapping("/users")
    public ResponseEntity<User> createUser(@RequestBody UserDTO dto) {
        User created = new User(dto);
        
        // Возвращаем 201 CREATED с заголовками
        return ResponseEntity
            .status(HttpStatus.CREATED)
            .header("X-Custom-Header", "value")
            .location(URI.create("/users/" + created.getId()))  // Location header
            .body(created);
    }
    
    @DeleteMapping("/users/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        
        // 204 No Content (успех, но нет тела ответа)
        return ResponseEntity.noContent().build();
    }
    
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUserWithCheck(@PathVariable Long id) {
        Optional<User> user = userService.findById(id);
        
        // Если не найден — 404 Not Found
        return user.map(ResponseEntity::ok)
                   .orElse(ResponseEntity.notFound().build());
    }
}

Характеристики:

  • Полный контроль над HTTP статусом
  • Контроль над заголовками
  • ✅ Можно возвращать разные статусы
  • ✅ Можно устанавливать Location header
  • ✅ Рекомендуется для production кода
  • ❌ Более многословный синтаксис
  • ❌ Нужно обрабатывать все случаи

Сравнительная таблица

Характеристика@ResponseBodyResponseEntity
Сложность синтаксиса✅ Простой⚠️ Сложнее
HTTP Status❌ Только 200✅ Любой (200, 201, 404, etc)
Заголовки❌ Нельзя установить✅ Полный контроль
Location header❌ Нет✅ Да (для created ресурсов)
Пустой ответ❌ Сложно (Void)✅ Легко (noContent().build())
Обработка ошибок⚠️ Нужны @ExceptionHandler✅ Встроено (if-else)
Conditional responses❌ Нельзя разные статусы✅ Да (if/else returns)
Рекомендуется❌ Простые CRUD✅ Production API

Примеры использования

@ResponseBody — простые случаи

@Controller
public class SimpleController {
    // Простое чтение
    @GetMapping("/api/users")
    @ResponseBody
    public List<User> listUsers() {
        return userService.findAll();
    }
    
    // Простое удаление (обычно возвращает 200)
    @DeleteMapping("/api/users/{id}")
    @ResponseBody
    public void deleteUser(@PathVariable Long id) {
        userService.delete(id);
    }
}

ResponseEntity — production API

@RestController
@RequestMapping("/api/users")
public class UserAPI {
    // Чтение с проверкой
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        return userService.findById(id)
            .map(ResponseEntity::ok)
            .orElseGet(() -> ResponseEntity.notFound().build());
    }
    
    // Создание с правильными заголовками
    @PostMapping
    public ResponseEntity<UserResponse> createUser(@Valid @RequestBody UserRequest req) {
        User created = userService.create(req);
        
        return ResponseEntity
            .created(URI.create("/api/users/" + created.getId()))
            .header("X-Resource-Id", created.getId().toString())
            .body(new UserResponse(created));
    }
    
    // Обновление
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(
            @PathVariable Long id,
            @Valid @RequestBody UserRequest req) {
        
        return userService.findById(id)
            .map(user -> {
                user.update(req);
                return ResponseEntity.ok(userService.save(user));
            })
            .orElseGet(() -> ResponseEntity.notFound().build());
    }
    
    // Удаление с правильным статусом
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        if (userService.findById(id).isPresent()) {
            userService.delete(id);
            return ResponseEntity.noContent().build();  // 204
        }
        return ResponseEntity.notFound().build();  // 404
    }
    
    // Поиск — может не найти
    @GetMapping("/search")
    public ResponseEntity<List<User>> search(
            @RequestParam String query) {
        
        List<User> results = userService.search(query);
        if (results.isEmpty()) {
            return ResponseEntity.noContent().build();  // 204 No Content
        }
        return ResponseEntity.ok(results);  // 200 with data
    }
}

REST API Best Practices — используй ResponseEntity

// ✅ ПРАВИЛЬНО — используем ResponseEntity
@RestController
@RequestMapping("/api/v1/orders")
public class OrderAPI {
    @PostMapping
    public ResponseEntity<OrderResponse> createOrder(@Valid @RequestBody OrderRequest req) {
        Order order = orderService.create(req);
        OrderResponse response = new OrderResponse(order);
        
        return ResponseEntity
            .created(URI.create("/api/v1/orders/" + order.getId()))
            .body(response);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<OrderResponse> getOrder(@PathVariable Long id) {
        return orderService.findById(id)
            .map(order -> ResponseEntity.ok(new OrderResponse(order)))
            .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build());
    }
}

// ❌ НЕПРАВИЛЬНО — только @ResponseBody
@Controller
public class BadOrderController {
    @GetMapping("/orders/{id}")
    @ResponseBody
    public Order getOrder(@PathVariable Long id) {
        // Что если заказ не найден? Будет 200 с null?
        // Нельзя вернуть 404
        return orderService.findById(id).orElse(null);
    }
}

@RestController vs @Controller

// @RestController = @Controller + @ResponseBody на все методы
@RestController  // ← современный способ
public class UserAPI {
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {  // Автоматически @ResponseBody
        return userService.findById(id).orElse(null);
    }
}

// Эквивалентно:
@Controller
public class UserAPI {
    @GetMapping("/users/{id}")
    @ResponseBody  // ← нужно явно
    public User getUser(@PathVariable Long id) {
        return userService.findById(id).orElse(null);
    }
}

Итоги

@ResponseBody:

  • Для простых случаев (всегда 200 OK)
  • Когда не нужны custom headers
  • В обучающих примерах

ResponseEntity:

  • Для production API
  • Когда нужны разные статусы
  • Когда нужны custom headers (Location, etc)
  • Рекомендуется всегда использовать

Правило: в современном Spring используй @RestController с ResponseEntity для всех API endpoints

В чем разница между ResponseEntity и ResponseBody? | PrepBro