Sergey Scream
Sergey Scream

Reputation: 45

React ajax request with multipart file and json data

I have a base request like this:

export const request = (options) => {
    const headers = new Headers({
        'Content-Type': 'application/json',
    });

    if (Common.getToken()) {
        headers.append('Authorization', 'Bearer ' + Common.getToken())
    }

    const defaults = {headers: headers};
    options = Object.assign({}, defaults, options);

    return fetch(options.url, options)
        .then(response =>
            response.json().then(json => {
                if (!response.ok) {
                    return Promise.reject(json);
                }
                return json;
            })
        );
};

and my ajax request:

onCreateNewPost(postDataRequest, photoBody) {
    const formData = new FormData();
    formData.append('photo', photoBody);
    formData.append('postData', JSON.stringify(postDataRequest));

    return request({
        url: API_BASE_URL + '/posts/new-post',
        method: 'POST',
        body: formData
    });
};

where postDataRequest - json object included post title, description etc... photoBody - image file. In the backend I have a controller's method:

@PostMapping(value = "/api/posts/new-post")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity createNewPost(@CurrentUser UserPrincipal currentUser,
                                    @RequestBody NewPostDataRequest postRequest,
                                    @RequestParam MultipartFile photo) {

    // method body


    return ResponseEntity.ok(new ActionCompleteResponse(true));
}

but when I send a request, I get Status Code: 400. What is the problem? I can separately send either json data or multipart data, but I can’t figure out how to transfer them together with one request. I tried to put headers without a Content-Type in the request, as in the code below, so that the request itself indicates it, but in response I get code 415.

onCreateNewPost(postDataRequest, photoBody) {
    const formData = new FormData();
    formData.append('photo', photoBody);
    formData.append('postData', JSON.stringify(postDataRequest));
    const headers = new Headers({});

    if (Common.getToken()) {
        headers.append('Authorization', 'Bearer ' + Common.getToken());
    }

    return request({
        url: API_BASE_URL + '/posts/new-post',
        headers: headers,
        method: 'POST',
        body: formData
    });
};

What should I do?

Upvotes: 1

Views: 2778

Answers (3)

user15426219
user15426219

Reputation: 21

@Sergey Scream solution is correct, I just want to add some information to clarify the problem.

So if you want to send json and a file using FormData you have to wrap your json in a blob including the type like he did:

formData.append('post', new Blob([JSON.stringify(postDataRequest)], {
            type: "application/json"
        }));

Adding your json like this will not work:

formData.append('post', JSON.stringify(postDataRequest));

Upvotes: 2

Sergey Scream
Sergey Scream

Reputation: 45

Okay, I found the solution: 1. Clear headers data (except Authorization token) 2. Add to @PostMapping consumes = MediaType.MULTIPART_FORM_DATA_VALUE and add @RequestPart to method parameter

ajax request like:

  onCreateNewPost(postDataRequest, photoBody) {
        const formData = new FormData();
        formData.append('post', new Blob([JSON.stringify(postDataRequest)], {
            type: "application/json"
        }));
        formData.append('photo', photoBody);
        const headers = new Headers({});

        if (Common.getToken()) {
            headers.append('Authorization', 'Bearer ' + Common.getToken())
        }

        return request({
            url: API_BASE_URL + '/posts/new-post',
            method: 'POST',
            headers: headers,
            body: formData
        });
    };

and spring controller like

@PostMapping(value = "/new-post", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@PreAuthorize("hasRole('USER')")
public ResponseEntity createNewPost(@CurrentUser UserPrincipal currentUser,
                                    @RequestPart("post") @Valid PostEntity post,
                                    @RequestPart("photo") @Valid MultipartFile photo) throws IOException {
    post.setAuthor(currentUser.getUsername());
    post.setAuthorId(currentUser.getId());
    post.setCommentsCount(0L);
    post.setDate(LocalDate.now());
    post.setPhoto(photo.getBytes());

    postService.save(post);

    return ResponseEntity.ok(new ActionCompleteResponse(true));
}

Upvotes: 1

k4rli
k4rli

Reputation: 120

You're setting Content-Type to application/json in request but body to form data in onCreateNewPost. If you removed line 3 from request then your current solution should work.

Also you're setting the Authorization header twice in onCreateNewPost and request.

Upvotes: 0

Related Questions