В чем разница аннотаций @Body и @Part в Retrofit
Что проверяют
Понимаете ли вы, как Retrofit собирает тело запроса и чем JSON-тело отличается от multipart/form-data. Следом почти всегда просят показать загрузку файла вместе с текстовыми полями и спрашивают, что будет, если смешать аннотации.
Как отвечать
Это два разных способа кодировать тело:
@Body: весь параметр становится телом запроса. Объект проходит через конвертер (GsonConverterFactory,MoshiConverterFactory, конвертер kotlinx.serialization) и уходит, например, какapplication/json. В методе допустим только один@Body, а готовыйRequestBodyотправится как есть;@Part: одна часть multipart-тела. Работает только в методе с@Multipart, частей может быть сколько угодно: файл, описание, идентификатор;MultipartBody.Part: файл обычно заворачивают черезMultipartBody.Part.createFormData("file", name, body), и тогда имя в@Partне пишут, оно уже внутри части;RequestBodyи другие типы: имя обязательно,@Part("description"). Для набора полей есть@PartMap.
Для полноты назовите третий вариант: @FormUrlEncoded с @Field для обычной формы без файлов. Правило простое: JSON-API значит @Body, файлы значат @Multipart и @Part.
Тело для файла в Kotlin создают через file.asRequestBody(mediaType) из OkHttp, оно читает файл потоком при отправке. Для Uri из системного пикера пути к файлу обычно нет, поэтому пишут свой RequestBody, который в writeTo копирует contentResolver.openInputStream(uri) в sink.
Где спотыкаются
Смешивают аннотации: @Part без @Multipart, @Body в multipart-методе или в @GET. Retrofit бросает IllegalArgumentException при первом вызове метода (или сразу, если включён validateEagerly), а кандидат не может объяснить причину.
Передают текстовое поле как @Part("description") description: String при подключённом Gson. Конвертер сериализует строку в JSON, и сервер получает значение в кавычках. Нужен RequestBody с text/plain или ScalarsConverterFactory, добавленный раньше Gson.
Читают файл целиком через readBytes() и заворачивают массив в тело. На видео или больших фото это OutOfMemoryError, хотя потоковое тело решает задачу без лишней памяти.