1

I have Spring boot 2 backend REST post API as follows:-

    @RequestMapping(value = "/profile", method = RequestMethod.POST)
public Profile saveOrUpdateProfile(@RequestParam("file") MultipartFile file, @RequestBody Profile profile, String path) {
    logger.info("file name:" + file.getOriginalFilename());
    if (file != null) {
        profile.setFileDownloadUri(profileService.storeFile(file, path));
        return profileService.saveOrUpdateProfile(profile);
    } else {
        return null;
    }
}

I am trying to call this from Reactjs as follows:-

  static postProfile(selectedFile, profile, replacePhotoPath) {
if (selectedFile) {
  let serverResponse;
  let formData = new FormData();
  formData.append("porofile", profile);
  formData.append("file", selectedFile);
  formData.append("path", replacePhotoPath);
  axios.post('http://localhost:8080/profile', formData, {
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  })
  .then(res => {
    console.log('res:' + res)
    serverResponse = res.data;
  })
  .catch((error) => {
    // handle this error
    console.log('error: ' + error);
    //setMessageFromServer(error.data)
   POST http://localhost:8080/profile 400 serverResponse = error.data;
  })
  return serverResponse;
}

}

I got an error as:

xhr.js:178 POST http://localhost:8080/profile 415

You may understand that, I am trying to post a profile with photo file. I am new in Reactjs and trying to learn by coding. I don't know how to make this axios post call to this API. If it is possible to do it by using another API than axios, it is fine for me. Thanks in advance!

Updated questions 1:- Endpoint:-

    @RequestMapping(value = "/profile2", method = RequestMethod.POST, consumes = MULTIPART_FORM_DATA)
public ResponseEntity<?> saveProfile(@RequestParam("file") MultipartFile file, @RequestParam("path") String path, @RequestParam("profile") Profile profile) {
    logger.info("file name:" + file.getOriginalFilename());
    return null;
}

Reactjs:-

static postProfile(selectedFile, replacePhotoPath, profile) {
if (selectedFile) {
  let serverResponse;
  let formData = new FormData();
  formData.append("file", selectedFile);
  formData.append("path", replacePhotoPath);
  formData.append("profile", profile)
  axios.post('http://localhost:8080/profile', formData, {
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  })
  .then(res => {
    console.log('res:' + res)
    serverResponse = res.data;
  })
  .catch((error) => {
    // handle this error
    console.log('error: ' + error);
    //setMessageFromServer(error.data)
    serverResponse = error.data;
  })
  return serverResponse;
}
}

I got diffrent error:-

POST http://localhost:8080/profile 400

Updated question 2:-

    @PostMapping("/profile2")
public void saveProfile(@ModelAttribute ProfileWrapper model) {
    logger.info("image: " + model.getImage());
    logger.info("profile: " + model.getProfile());
}

Reactjs:-

  static postData(selectedFile, replacePhotoPath, profile) {
if (selectedFile) {
  let serverResponse;
  let formData = new FormData();
  formData.append("porofile", profile);
  formData.append("file", selectedFile);
  formData.append("path", replacePhotoPath);
  axios.post('http://localhost:8080/profile', formData, {
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  })
  .then(res => {
    console.log('res:' + res)
    serverResponse = res.data;
    //this.postProfile(data, selectedCity, history);
  })
  .catch((error) => {
    // handle this error
    console.log('error: ' + error);
    //setMessageFromServer(error.data)
    serverResponse = error.data;
  })
  return serverResponse;
}

}

This time where was no error, it could reach the endpoint. But the ProfileWrapper model members are null. So the log printed:-

: image: null : profile: null

Then I updated Reactjs as:-

  static postData(selectedFile, replacePhotoPath, profile, selectedCity, history) {
if (selectedFile) {
  let serverResponse;
  let wrappe = {
    "profile": profile,
    "image" : selectedFile,
    "path": replacePhotoPath 
  }
  // let formData = new FormData();
  // formData.append("porofile", profile);
  // formData.append("file", selectedFile);
  // formData.append("path", replacePhotoPath);
  axios.post('http://localhost:8080/profile', wrappe, {
    // headers: {
    //   'Content-Type': 'multipart/form-data'
    // }
  })
  .then(res => {
    console.log('res:' + res)
    serverResponse = res.data;
    //this.postProfile(data, selectedCity, history);
  })
  .catch((error) => {
    // handle this error
    console.log('error: ' + error);
    //setMessageFromServer(error.data)
    serverResponse = error.data;
  })
  return serverResponse;
}
}

It is the same again no error, but it arrives at the endpoint with the model's member null. To make sure, I debug the frontend code to check that I am filling correct data and it is not null. All looks fine.

Update 3:-

Btw, I removed the String path member from both back and front.

public class ProfileWrapper {

private MultipartFile image;
private Profile profile;

public ProfileWrapper() {
}

public ProfileWrapper( MultipartFile image, Profile profile) {
    this.image = image;
    this.profile = profile;

}

public Profile getProfile() {
    return profile;
}

public void setProfile(Profile profile) {
    this.profile = profile;
}

public MultipartFile getImage() {
    return image;
}

public void setImage(MultipartFile image) {
    this.image = image;
}
}

Btw I updated the endpoint to @RequestBody. Then I got this error:-

[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.springframework.web.multipart.MultipartFile]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of org.springframework.web.multipart.MultipartFile (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: (PushbackInputStream); line: 1, column: 269] (through reference chain: com.profile.wrapper.ProfileWrapper["image"])] with root cause

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of org.springframework.web.multipart.MultipartFile (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: (PushbackInputStream); line: 1, column: 269] (through reference chain: com.profile.wrapper.ProfileWrapper["image"])

masiboo
  • 4,537
  • 9
  • 75
  • 136
  • Your spring boot seems off, you have file as a request Param, but you're sending it as form data. And then you have profile as request body. Your axios code is fine, it's your spring controller causing problems – Thomas__ Mar 07 '20 at 21:53
  • So could u suggest what kind of change do I need in spring boot? There I have to get an image file, profile and a string path. – masiboo Mar 07 '20 at 22:07
  • https://spring.io/guides/gs/handling-form-submission/ have a pojo similar to `Greeting` in that example, that has variable for file, profile, and string path – Thomas__ Mar 07 '20 at 22:13
  • In this link, it has simple pojo with 2 string members. If I reflector my code and make 2 post call as ¸ ` @RequestMapping(value = "/photo", method = RequestMethod.POST) public String saveOrUpdatePhoto(@RequestParam("file") MultipartFile file, String path) {` and `@RequestMapping(value = "/profile", method = RequestMethod.POST) public ResponseEntity> saveOrUpdateProfile(@RequestBody Profile profile) {` then it works. But I don't know how to combine both. – masiboo Mar 08 '20 at 08:29
  • Yea actually pojo might not work for MultipartFile, haven't tested it, but the following should work: `@RequestMapping(value = "/profile", method = RequestMethod.POST, consumes=MULTIPART_FORM_DATA) public ResponseEntity> saveProfile(@RequestParam("file") MultipartFile file, @RequestParam("path") String path, @RequestParam("profile") Profile profile) {` – Thomas__ Mar 08 '20 at 08:49
  • Also you have a typo in the axios, `formData.append("porofile", profile)` ~> `formData.append("profile", profile)` – Thomas__ Mar 08 '20 at 08:52
  • pls check the updated question part. I tried as u suggested and got a new error. – masiboo Mar 08 '20 at 09:35
  • You're returning null from controller which will come as bad request/400 status, check to see if your log message is displayed. – Thomas__ Mar 08 '20 at 09:41
  • But yea you need to check your spring boot logs, it should give the answer – Thomas__ Mar 08 '20 at 09:44
  • Well, I didn't add any implementation for this api. I just wanted to reach it from the frontend. I added a breakpoint and tried to debug it. But it never comes in. I made the return type void and tested. it is same. – masiboo Mar 08 '20 at 09:46
  • Check out the logs on spring boot, maybe it cannot serialise the profile or something. – Thomas__ Mar 08 '20 at 09:49
  • .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'com.profile.model.Profile'; – masiboo Mar 08 '20 at 09:53
  • Yea was thinking it'd be something like that, you're trying to send a profile object as form data, this might guide you https://stackoverflow.com/a/49991403/1434072 pojo solution might be best bet, but you might need to create a custom converter for profile, as the frontend will be sending a string – Thomas__ Mar 08 '20 at 09:59
  • Pls, check again updated questions. it is good that there was no error, but the wrapper model's member are null. – masiboo Mar 08 '20 at 11:16
  • Well you took out the form-data header so you'll need to update controller to be `@RequestBody` Vs `@ModelAttribute`, as now you'll be sending it as json – Thomas__ Mar 08 '20 at 11:23
  • And be better to share your ProfileWrapper object – Thomas__ Mar 08 '20 at 11:24
  • pls check new error – masiboo Mar 08 '20 at 11:51
  • You keep going back and forth between mixing something that is form data and JSON. Your profile sounds like a JSON object. Your best bet is to follow the stackoverflow.com/a/49991403/1434072 and send profile as a string `JSON.stringify()` then map it to a profile object in the backend, that way you can send files as multipart/form-data as it really is the only way, or send the file as base64 encoded, then decide it in the backend. So pojo would have file as a String, then you decode it in to a file – Thomas__ Mar 08 '20 at 12:26

0 Answers0