Spring: Использование RequestPart вместо RequestBody
Не могу на сервере принять запрос из клиента с телом Json Object & MultipartFile. Выдает следующие ошибки:
The request was rejected because no multipart boundary was found:
formData.append('idea', JSON.stringify(idea));
formData.append('addImages', file);
headers: {
'Content-Type': 'multipart/mixed'
}
Required request part 'idea' is not present:
formData.append('idea', JSON.stringify(idea));
formData.append('addImages', file);
headers: {
'Content-Type': 'multipart/mixed;boundary=gc0p4Jq0M2Yt08jU534c0p'
}
Content type 'application/octet-stream not supported:
formData.append('idea', JSON.stringify(idea));
formData.append('addImages', file);
Controller
@PostMapping("/idea/save")
public ResponseEntity<Idea> saveIdea(@RequestPart("idea") @Valid Idea idea,
@RequestPart(required = false) List<MultipartFile> addImages,
@RequestPart(required = false) List<MultipartFile> addFiles,
Principal principal) {
return new ResponseEntity<>(ideaService.add(idea, addImages, addFiles, principal.getName()), HttpStatus.CREATED);
}
Скорее всего нужно добавить спрингу какой-то конвертер и применить случай 1 или 2. Если это так, то дайте наводку на чертеж.
Ответы (2 шт):
Решил с помощью реализации AbstractJackson2HttpMessageConverter
AbstractJackson2HttpMessageConverter
@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
/**
* Converter for support http request with header Content-Type: multipart/form-data
*/
public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
protected boolean canWrite(MediaType mediaType) {
return false;
}
}
Controller
@PostMapping("/idea/save")
public ResponseEntity<Idea> saveIdea(@RequestPart("idea") @Valid Idea idea,
@RequestPart(required = false) List<MultipartFile> addImages,
@RequestPart(required = false) List<MultipartFile> addFiles,
Principal principal) {
return new ResponseEntity<>(ideaService.add(idea, addImages, addFiles, principal.getName()), HttpStatus.CREATED);
}
Client
public put(idea: Idea, formData: FormData): Observable<Idea> {
formData.append('addImages', this.addImages);
formData.append('addImages', this.addFiles);
formData.append('idea', JSON.stringify(idea));
return this.httpClient.put<Idea>(`${this.apiBaseUrl}/idea/put`, formData);
}
Столкнулся с подобной проблемой. Отправлял в одном запросе файлы и JSON. Не знал, что для каждой строки из form-data можно указывать content-type отдельно. Когда установил content-type для моего JSON, спринг сам смог распарсить объект без явного указания, как ему это делать (мапперы, конвертеры).
Эндпоинт на java spring framework (boot)
@RestController
@RequestMapping("/api/v1/spot")
//...
@PostMapping
public ResponseEntity<?> createSpotForModeration(
@RequestPart("spot") NewSpotDTO dto,
@RequestPart("files") List<MultipartFile> files) {...}
Запрос через curl, для spot указываем явно content-type (type=application/json). Для картинок curl, насколько я понял, сам установит multipart/form-data + boundary
curl --location 'localhost:8080/api/v1/spot' \
--form 'files=@"/some-path/img1.jpg"' \
--form 'files=@"/some-path/img2.jpeg"' \
--form 'spot="{
\"name\": \"Имя некоторой сущности\",
\"description\": \"Описание некоторой сущности\"
}";type=application/json'
P.S. делая, запрос в postman, нужно ставить галочку и устанавливать content-type вручную для каждого form-data.
Надеюсь, мое дополнение кому-то поможет.