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

Какая из концепций является ключевым для REST API?

1.0 Junior🔥 171 комментариев
#REST API и микросервисы

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

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

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

Какая из концепций является ключевым для REST API?

REST (Representational State Transfer) — архитектурный стиль для построения веб-сервисов. Давайте разберём ключевые концепции, которые делают API действительно RESTful.

Основные ключевые концепции REST

1. Ресурсы (Resources) — САМАЯ КЛЮЧЕВАЯ КОНЦЕПЦИЯ

Ресурсы — это сердце REST. Всё в REST моделируется как ресурсы:

// Ресурс "User"
GET /api/v1/users/123
// Запрашиваем конкретного пользователя с ID 123

// Ресурс "Orders for User"
GET /api/v1/users/123/orders
// Запрашиваем заказы этого пользователя

// Ресурс "Comment on Post"
GET /api/v1/posts/456/comments/789
// Вложенный ресурс: комментарий 789 на посте 456

2. HTTP методы (Uniform Interface)

REST использует стандартные HTTP методы для операций:

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    
    // GET — получить ресурс (safe, idempotent)
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    // POST — создать новый ресурс (не idempotent)
    @PostMapping
    public User createUser(@RequestBody CreateUserRequest request) {
        return userService.create(request);
    }
    
    // PUT — полностью заменить ресурс (idempotent)
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody UpdateUserRequest request) {
        return userService.update(id, request);
    }
    
    // PATCH — частично обновить ресурс (может быть idempotent)
    @PatchMapping("/{id}")
    public User patchUser(@PathVariable Long id, @RequestBody JsonPatch patch) {
        return userService.patch(id, patch);
    }
    
    // DELETE — удалить ресурс (idempotent)
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.delete(id);
    }
}

3. Stateless (Без состояния)

Каждый запрос содержит всю информацию, необходимую для его обработки:

// Плохо - зависит от состояния сессии
@GetMapping("/next-page")
public Page<User> getNextPage() {
    // Какая была предыдущая страница? Откуда мне знать?
    return userService.getNextPage();
}

// Хорошо - вся информация в запросе
@GetMapping("/users")
public Page<User> getUsers(
    @RequestParam(defaultValue = "0") int page,
    @RequestParam(defaultValue = "20") int size,
    @RequestParam(defaultValue = "name") String sort
) {
    return userService.getUsers(page, size, sort);
}

4. Representation (Представление ресурсов)

Ресурсы отправляются в определённом представлении (обычно JSON):

// Один ресурс может быть представлен несколькими способами
@GetMapping(value = "/users/{id}", produces = {
    MediaType.APPLICATION_JSON_VALUE,
    MediaType.APPLICATION_XML_VALUE
})
public User getUser(@PathVariable Long id, 
                   @RequestHeader(value = "Accept") String acceptHeader) {
    return userService.findById(id);
    // Представление зависит от Accept header
}

5. HATEOAS (Hypermedia As The Engine Of Application State)

Ответы содержат ссылки на связанные ресурсы:

@GetMapping("/users/{id}")
public EntityModel<User> getUser(@PathVariable Long id) {
    User user = userService.findById(id);
    
    return EntityModel.of(user,
        linkTo(methodOn(UserController.class).getUser(id)).withSelfRel(),
        linkTo(methodOn(UserController.class).getAllUsers()).withRel("users"),
        linkTo(methodOn(OrderController.class).getUserOrders(id)).withRel("orders")
    );
}

// Response:
// {
//   "id": 1,
//   "name": "John",
//   "email": "john@example.com",
//   "_links": {
//     "self": { "href": "/api/v1/users/1" },
//     "users": { "href": "/api/v1/users" },
//     "orders": { "href": "/api/v1/users/1/orders" }
//   }
// }

6. Content Negotiation

Сервер и клиент договариваются о формате данных:

@GetMapping("/users/{id}")
@JsonView(User.PublicView.class)
public User getUser(@PathVariable Long id) {
    return userService.findById(id);
}

// Или через Accept header
@GetMapping(value = "/users/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public User getUserAsJson(@PathVariable Long id) { ... }

@GetMapping(value = "/users/{id}", produces = MediaType.APPLICATION_XML_VALUE)
public User getUserAsXml(@PathVariable Long id) { ... }

Практический пример RESTful API

@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {
    
    @Autowired
    private OrderService orderService;
    
    // GET /api/v1/orders — список всех заказов (пагинация)
    @GetMapping
    public Page<Order> listOrders(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "20") int size
    ) {
        return orderService.listOrders(page, size);
    }
    
    // GET /api/v1/orders/123 — получить конкретный заказ
    @GetMapping("/{id}")
    public Order getOrder(@PathVariable UUID id) {
        return orderService.findById(id)
            .orElseThrow(() -> new OrderNotFoundException(id));
    }
    
    // POST /api/v1/orders — создать новый заказ
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Order createOrder(@Valid @RequestBody CreateOrderRequest request) {
        return orderService.create(request);
    }
    
    // PUT /api/v1/orders/123 — полностью обновить заказ
    @PutMapping("/{id}")
    public Order updateOrder(@PathVariable UUID id, 
                            @Valid @RequestBody UpdateOrderRequest request) {
        return orderService.update(id, request);
    }
    
    // PATCH /api/v1/orders/123 — частично обновить (например, только статус)
    @PatchMapping("/{id}/status")
    public Order updateOrderStatus(@PathVariable UUID id, 
                                  @RequestBody OrderStatus newStatus) {
        return orderService.updateStatus(id, newStatus);
    }
    
    // DELETE /api/v1/orders/123 — удалить заказ
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteOrder(@PathVariable UUID id) {
        orderService.delete(id);
    }
    
    // GET /api/v1/orders/123/items — вложенный ресурс
    @GetMapping("/{orderId}/items")
    public List<OrderItem> getOrderItems(@PathVariable UUID orderId) {
        return orderService.getItems(orderId);
    }
}

HTTP Status Codes — важная часть REST

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    // 200 OK — успешный GET
    // 201 Created — успешный POST (создан ресурс)
    // 204 No Content — успешный DELETE или PATCH без тела ответа
    
    @ExceptionHandler(OrderNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)  // 404
    public ErrorResponse handleNotFound(OrderNotFoundException e) {
        return new ErrorResponse("Order not found", e.getMessage());
    }
    
    @ExceptionHandler(InvalidOrderException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)  // 400
    public ErrorResponse handleBadRequest(InvalidOrderException e) {
        return new ErrorResponse("Invalid order", e.getMessage());
    }
    
    @ExceptionHandler(UnauthorizedException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)  // 401
    public ErrorResponse handleUnauthorized(UnauthorizedException e) {
        return new ErrorResponse("Unauthorized", e.getMessage());
    }
    
    @ExceptionHandler(ForbiddenException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)  // 403
    public ErrorResponse handleForbidden(ForbiddenException e) {
        return new ErrorResponse("Forbidden", e.getMessage());
    }
}

Best Practices для REST API

  • Используй существительные для URL, не глаголы:

    • POST /users (создать)
    • DELETE /users/123 (удалить)
    • GET /getUsers
    • POST /createUser
  • Версионируй API: /api/v1/, /api/v2/

  • Используй правильные HTTP статусы — это часть REST контракта

  • Документируй через Swagger/OpenAPI

  • Кэширование: используй Cache-Control и ETag заголовки

  • Paging и Filtering: ?page=0&size=20&sort=name,desc

Главное: REST основана на идее стандартизации использования HTTP для взаимодействия с ресурсами. Ключевая концепция — это именно Ресурсы, к которым применяются стандартные HTTP методы.