I'm trying to send through multipart/form-data a post request from my products controller, where I upload a file of images and information of my product in json
@RestController
@CrossOrigin(origins = "*", maxAge = 3600)
@RequestMapping("/product")
public class ProductController {
final ProductService productService;
final CategoryService categoryService;
final ProductMapper productMapper;
final S3Client s3Client;
private final String BUCKET_NAME = "awstockproducts" + System.currentTimeMillis();
public ProductController(ProductService productService, ProductMapper productMapper, CategoryService categoryService, S3Client s3Client) {
this.productService = productService;
this.productMapper = productMapper;
this.categoryService = categoryService;
this.s3Client = s3Client;
}
@PostMapping(MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Object> saveProduct (@RequestPart("productDto") @Valid ProductDto productDto, @RequestPart(value = "file")MultipartFile file) {
try {
if (productService.existsByProduct(productDto.getProduct())) {
return ResponseEntity.status(HttpStatus.CONFLICT).body("Product already exists!");
}
ProductModel productModel = productMapper.toProductModel(productDto);
CategoryModel categoryModel = categoryService.findById(productDto.getProductCategory().getCategory_id())
.orElseThrow(() -> new RuntimeException("Category not found"));
productModel.setProductCategory(categoryModel);
String fileName = "/products/images/" + UUID.randomUUID().toString() + "-" + file.getOriginalFilename();
s3Client.putObject(PutObjectRequest
.builder()
.bucket(BUCKET_NAME)
.key(fileName)
.build(),
software.amazon.awssdk.core.sync.RequestBody.fromString("Testing java sdk"));
return ResponseEntity.status(HttpStatus.CREATED).body(productService.save(productModel));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.CONFLICT).body("Cannot create product. Check if the fields sent in your request are correct.");
}
}
in my postman I'm sending it like this, and getting the error 405
In my console I am getting the error:
Request method 'POST' is not supported]
I don't understand why since I am sending a postMapping
updated error in postman:
{
"cause": null,
"stackTrace": [
{
"classLoaderName": "app",
"moduleName": null,
"moduleVersion": null,
"methodName": "from",
"fileName": "UnrecognizedPropertyException.java",
"lineNumber": 61,
"nativeMethod": false,
"className": "com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException"
},
{
"classLoaderName": "app",
"moduleName": null,
"moduleVersion": null,
"methodName": "handleUnknownProperty",
"fileName": "DeserializationContext.java",
"lineNumber": 1132,
"nativeMethod": false,
"className": "com.fasterxml.jackson.databind.DeserializationContext"
},
{
"classLoaderName": "app",
"moduleName": null,
"moduleVersion": null,
"methodName": "handleUnknownProperty",
"fileName": "StdDeserializer.java",
"lineNumber": 2202,
"nativeMethod": false,
"className": "com.fasterxml.jackson.databind.deser.std.StdDeserializer"
}, ...
Change your @PostMapping(MediaType.MULTIPART_FORM_DATA_VALUE)
because putting the media type there will actually map the URL to /product/multipart/form-data
and not /product/
change it to this
@PostMapping(value = "/", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
also, you need to update the method signature to something like this (Change @RequestPart to @RequestParam)
@PostMapping(value = "/", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Object> saveProduct (@RequestParam("productDto") String jsonString, @RequestParam("file") MultipartFile file) {
ObjectMapper objectMapper = new ObjectMapper();
ProductDto productDto = objectMapper.readValue(jsonString, ProductDto.class);
}
and make sure you have the relevant dependencies commons-fileupload and commons-io
also I recomemend when Uploading set a maximum upload size for the file you can do it like this:
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1024 * 1024 * 5); // 5 MB
return resolver;
}
and then in postman call http://localhost:8078/product/
A second solution you can create the suitable converter in Spring: the solution is like
public class ProductDtoConverter extends AbstractHttpMessageConverter<ProductDto> {
public ProductDtoConverter() {
super(MediaType.APPLICATION_JSON);
}
@Override
protected boolean supports(Class<?> clazz) {
return ProductDto.class.isAssignableFrom(clazz);
}
@Override
protected ProductDto readInternal(Class<? extends ProductDto> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
String productDtoString = IOUtils.toString(inputMessage.getBody(), StandardCharsets.UTF_8);
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(productDtoString, ProductDto.class);
}
}
and in you AppConfig:
@Configuration
public class AppConfig {
@Bean
public ProductDtoConverter productDtoConverter() {
return new ProductDtoConverter();
}
}