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