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

Что такое PATCH-запрос?

1.3 Junior🔥 181 комментариев
#Работа с сетью

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

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

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

PATCH-запрос: Полный гайд

Определение

PATCH — это HTTP метод для частичного обновления ресурса на сервере. В отличие от PUT, который заменяет весь ресурс, PATCH изменяет только указанные поля.

PUT /users/123     → Заменить весь user целиком
PATCH /users/123   → Обновить только указанные поля user'а

PATCH vs PUT vs POST

PUT — полное обновление

// Заменить весь ресурс
var user = {
  'id': 123,
  'name': 'John',
  'email': 'john@example.com',
  'age': 30,
  'city': 'New York',
};

var response = await http.put(
  Uri.parse('https://api.example.com/users/123'),
  headers: {'Content-Type': 'application/json'},
  body: jsonEncode(user), // Отправляем ВСЕ поля
);

// На сервере: весь user заменен

PATCH — частичное обновление

// Обновить только name и email
var updates = {
  'name': 'Jane',
  'email': 'jane@example.com',
};

var response = await http.patch(
  Uri.parse('https://api.example.com/users/123'),
  headers: {'Content-Type': 'application/json'},
  body: jsonEncode(updates), // Только измененные поля
);

// На сервере: age и city остаются прежними
// Результат:
// {
//   'id': 123,
//   'name': 'Jane',        ← изменилось
//   'email': 'jane@example.com',  ← изменилось
//   'age': 30,             ← осталось
//   'city': 'New York',    ← осталось
// }

POST — создание нового ресурса

// Создать новый user
var newUser = {
  'name': 'Bob',
  'email': 'bob@example.com',
};

var response = await http.post(
  Uri.parse('https://api.example.com/users'),
  headers: {'Content-Type': 'application/json'},
  body: jsonEncode(newUser),
);

// На сервере: создан новый user (с новым id)

Таблица сравнения

МетодНазначениеЗаменяет весь ресурс?Может создать?Может быть идемпотентным?
POSTСозданиеN/A
PUTПолное обновление
PATCHЧастичное обновление
DELETEУдаление

PATCH в Dart/Flutter

Базовый пример с http пакетом

import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> updateUserEmail(String userId, String newEmail) async {
  final response = await http.patch(
    Uri.parse('https://api.example.com/users/$userId'),
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer $token',
    },
    body: jsonEncode({
      'email': newEmail,
    }),
  );
  
  if (response.statusCode == 200) {
    print('Email updated successfully');
    final updatedUser = jsonDecode(response.body);
    print(updatedUser);
  } else {
    throw Exception('Failed to update email: ${response.statusCode}');
  }
}

С Dio пакетом (более удобно)

import 'package:dio/dio.dart';

final dio = Dio();

Future<Map<String, dynamic>> patchUser(
  String userId,
  Map<String, dynamic> updates,
) async {
  try {
    final response = await dio.patch(
      'https://api.example.com/users/$userId',
      data: updates,
      options: Options(
        headers: {'Authorization': 'Bearer $token'},
      ),
    );
    
    return response.data as Map<String, dynamic>;
  } on DioException catch (e) {
    throw Exception('PATCH failed: ${e.message}');
  }
}

// Использование
var updatedUser = await patchUser('123', {
  'name': 'Jane',
  'age': 31,
});

Практические примеры

1. Обновление профиля пользователя

class UserRepository {
  final Dio _dio;
  
  UserRepository(this._dio);
  
  Future<User> updateProfile(String userId, UserUpdateRequest request) async {
    try {
      final response = await _dio.patch(
        '/users/$userId',
        data: {
          if (request.name != null) 'name': request.name,
          if (request.email != null) 'email': request.email,
          if (request.phone != null) 'phone': request.phone,
          if (request.bio != null) 'bio': request.bio,
        },
      );
      
      return User.fromJson(response.data);
    } catch (e) {
      throw UserRepositoryException('Failed to update profile: $e');
    }
  }
}

class UserUpdateRequest {
  final String? name;
  final String? email;
  final String? phone;
  final String? bio;
  
  UserUpdateRequest({
    this.name,
    this.email,
    this.phone,
    this.bio,
  });
}

// Использование
final repo = UserRepository(dio);
var updatedUser = await repo.updateProfile('123', UserUpdateRequest(
  name: 'Jane',
  bio: 'Flutter Developer',
));

2. Обновление только одного поля

Future<void> changePassword(String userId, String oldPassword, String newPassword) async {
  final response = await http.patch(
    Uri.parse('https://api.example.com/users/$userId/password'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({
      'oldPassword': oldPassword,
      'newPassword': newPassword,
    }),
  );
  
  if (response.statusCode != 200) {
    throw Exception('Password change failed');
  }
}

3. Обновление статуса заказа

Future<Order> updateOrderStatus(String orderId, OrderStatus status) async {
  final response = await http.patch(
    Uri.parse('https://api.example.com/orders/$orderId'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({
      'status': status.toString(),
      'updatedAt': DateTime.now().toIso8601String(),
    }),
  );
  
  if (response.statusCode == 200) {
    return Order.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Order update failed: ${response.statusCode}');
  }
}

JSON Merge Patch (RFC 7386)

Eсли нужны более сложные операции, можно использовать стандарт JSON Merge Patch:

// Клиент отправляет частичное обновление
var patch = {
  'address': {
    'city': 'Boston', // Только город меняется
  },
};

// Сервер должен правильно мерджить:
// Было: {'address': {'city': 'New York', 'zip': '10001'}}
// Результат: {'address': {'city': 'Boston', 'zip': '10001'}}

Ошибки и best practices

❌ Ошибка 1: Отправить полный ресурс вместо обновления

// НЕПРАВИЛЬНО - отправляем весь объект
var fullUser = await fetchUser('123'); // Получаем весь user
fullUser.name = 'Jane';

await http.patch(
  Uri.parse('https://api.example.com/users/123'),
  body: jsonEncode(fullUser), // Отправляем ВСЕ поля!
);

✅ Правильно:

// Отправляем только изменения
await http.patch(
  Uri.parse('https://api.example.com/users/123'),
  body: jsonEncode({'name': 'Jane'}), // Только название
);

❌ Ошибка 2: Забыть заголовки

var response = await http.patch(
  Uri.parse('https://api.example.com/users/123'),
  // ❌ ЗАБЫЛИ headers!
  body: jsonEncode(updates),
);

✅ Правильно:

var response = await http.patch(
  Uri.parse('https://api.example.com/users/123'),
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer $token',
  },
  body: jsonEncode(updates),
);

❌ Ошибка 3: Не обработать ошибки

var response = await http.patch(...);
var user = User.fromJson(jsonDecode(response.body)); // Падет если 400!

✅ Правильно:

var response = await http.patch(...);

if (response.statusCode == 200) {
  var user = User.fromJson(jsonDecode(response.body));
} else if (response.statusCode == 400) {
  print('Invalid data');
} else if (response.statusCode == 404) {
  print('User not found');
} else if (response.statusCode == 500) {
  print('Server error');
} else {
  throw Exception('Unexpected error: ${response.statusCode}');
}

HTTP Status codes для PATCH

CodeЗначениеДействие
200OKУспешное обновление
204No ContentУспешно, но нет данных в ответе
400Bad RequestНевалидные данные
401UnauthorizedНе авторизован
403ForbiddenНет доступа
404Not FoundРесурс не найден
409ConflictКонфликт (версия изменилась)
500Internal Server ErrorОшибка сервера

Идемпотентность PATCH

PATCH может быть идемпотентным, но не всегда:

// Идемпотентный PATCH (результат одинаков)
var updates = {'name': 'Jane', 'status': 'active'};
await http.patch(url, body: jsonEncode(updates)); // Результат 1
await http.patch(url, body: jsonEncode(updates)); // Результат 2 = Результат 1 ✅

// Неидемпотентный PATCH (результат разный)
var increment = {'count': {'+': 1}}; // Увеличить count на 1
await http.patch(url, body: jsonEncode(increment)); // count = 1
await http.patch(url, body: jsonEncode(increment)); // count = 2 ❌

Итоги

PATCH-запрос:

  • Частичное обновление ресурса (в отличие от PUT)
  • Отправляем только изменяемые поля
  • Эффективнее чем PUT для небольших обновлений
  • Требует правильной обработки ошибок
  • Идемпотентность зависит от реализации сервера
  • Основной способ обновления в REST API

B Flutter используй http или dio для PATCH запросов, всегда указывай Content-Type и обрабатывай ошибки правильно!