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

Является REST API stateful или stateless

2.0 Middle🔥 251 комментариев
#REST API и микросервисы#Spring Boot и Spring Data

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

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

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

REST API: Stateless архитектура

Ключевой принцип REST

REST API должна быть stateless — это один из фундаментальных принципов RESTful архитектуры, определённый Roy Fielding в его диссертации в 2000 году. Stateless означает, что каждый запрос содержит всю информацию, необходимую серверу для его обработки, без зависимости от предыдущих запросов.

Stateless vs Stateful

Stateless (REST)

// Каждый запрос независим и содержит всю необходимую информацию
GET /api/users/123
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

// Сервер обрабатывает запрос, не сохраняя контекст о клиенте
public class UserController {
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id,
                       @RequestHeader("Authorization") String token) {
        User user = userService.findById(id);
        if (user == null) {
            throw new UserNotFoundException(id);
        }
        return user;  // Ответ полностью описан в заголовках и теле
    }
}

Stateful (не REST)

// Сервер сохраняет состояние сессии о клиенте
POST /api/login
{"username": "john", "password": "secret"}
// Ответ: Set-Cookie: JSESSIONID=xyz123

GET /api/users/123
// Cookie: JSESSIONID=xyz123
// Сервер ищет сессию в памяти и использует сохранённую информацию о пользователе

Почему REST именно stateless

1. Масштабируемость

Без состояния на сервере просто распределить запросы между несколькими инстанциями:

// Архитектура Stateless REST
┌─────────────┐
│  Client     │
└──────┬──────┘
       │ GET /api/users/123
       │ Authorization: Bearer token
       ↓
    [Load Balancer]
       │
   ┌───┴────┬──────────┬──────────┐
   ↓        ↓          ↓          ↓
[Server1][Server2][Server3][Server4]

// Каждый сервер может обработать любой запрос
// Нет привязки клиента к конкретному серверу

В сравнении со stateful архитектурой:

// Stateful потребует Sticky Sessions
Client → Server1 (сессия в памяти Server1)
// Если Server1 упадёт, сессия потеряется
// Нужны сложные решения: session replication, shared session store

2. Надёжность и отказоустойчивость

// Stateless: любой сервер может обработать запрос
public class OrderService {
    public Order getOrder(Long orderId, String token) {
        // Проверяем токен (не храним сессию)
        User user = jwtTokenProvider.validateAndExtract(token);
        
        // Получаем заказ из БД
        Order order = orderRepository.findById(orderId);
        
        // Проверяем права доступа
        if (!order.getUserId().equals(user.getId())) {
            throw new AccessDeniedException();
        }
        return order;
    }
}

// Если Server1 упал, но данные в БД сохранены,
// любой другой сервер может обработать этот же запрос

3. Простота развёртывания и масштабирования

// Stateless: горизонтальное масштабирование просто
// Добавляем новый сервер → Load Balancer начинает отправлять запросы
// Никаких сложностей с синхронизацией состояния

// Пример конфигурации Spring Boot
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()  // CSRF не нужна в stateless API
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)  // Это ключевой параметр
            .and()
            .authorizeRequests()
                .antMatchers("/api/**").authenticated()
            .and()
            .addFilterBefore(
                new JwtAuthenticationFilter(),
                UsernamePasswordAuthenticationFilter.class
            );
        return http.build();
    }
}

Как достигается Stateless в REST

1. Аутентификация через токены

// JWT (JSON Web Token) содержит всю информацию о пользователе
public class JwtTokenProvider {
    public String generateToken(User user) {
        return Jwts.builder()
            .setSubject(user.getId().toString())
            .claim("username", user.getUsername())
            .claim("roles", user.getRoles())
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 86400000))
            .signWith(SignatureAlgorithm.HS512, "secret-key")
            .compact();
    }
    
    public Claims getClaimsFromToken(String token) {
        return Jwts.parser()
            .setSigningKey("secret-key")
            .parseClaimsJws(token)
            .getBody();
    }
}

// Каждый запрос включает этот токен
GET /api/orders
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9...

// Сервер не хранит ничего о клиенте, всё есть в токене

2. Все необходимые данные в запросе

public class PaymentController {
    @PostMapping("/payments")
    public PaymentResponse createPayment(
            @RequestBody PaymentRequest request,  // ВСЕ данные здесь
            @RequestHeader("Authorization") String token) {
        
        // Нет привязки к сессии, нет глобального состояния
        // Каждый запрос полностью независим
        User user = extractUserFromToken(token);
        return paymentService.process(request, user);
    }
}

3. Идемпотентность

// Stateless API часто использует Idempotency-Key
POST /api/payments
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{
    "amount": 100,
    "orderId": 123
}

// Сервер может безопасно обработать дублирующиеся запросы
public class PaymentService {
    public Payment createPayment(PaymentRequest request, String idempotencyKey) {
        // Проверяем, не обработали ли мы этот платёж ранее
        Payment existing = paymentRepository.findByIdempotencyKey(idempotencyKey);
        if (existing != null) {
            return existing;  // Возвращаем тот же результат
        }
        
        // Обрабатываем новый платёж
        Payment payment = new Payment();
        payment.setIdempotencyKey(idempotencyKey);
        return paymentRepository.save(payment);
    }
}

Исключение: когда нужно состояние

В редких случаях REST API может иметь элементы состояния:

// WebSocket — исключение, требует сохранения соединения
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new ChatWebSocketHandler(), "/chat")
            .setAllowedOrigins("*");
    }
}

Заключение

REST API — принципиально stateless архитектура. Это обеспечивает масштабируемость, надёжность и простоту развёртывания. Стател достигается через использование токенов (JWT), включение всех необходимых данных в каждый запрос и избежание сохранения клиентского контекста на сервере.