React ajax request with multipart file and json data - javascript

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?

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));
}

#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));

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.

Related

Send file to (fetch to c# web api)

I tried to send file by JS Fetxh API to ASP .NET 6 WebAPI and get 400 status.
let data = new FormData()
data.append('file', file)
const response = await fetch('https://localhost:7054/Pictures',
{
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data'
},
body: data
});
[HttpPost]
public async Task<ActionResult> Index([FromBody]IFormFile file)
{
try
{
using (var fs = new FileStream(dir, FileMode.Create))
{
await file.CopyToAsync(fs);
}
return StatusCode(StatusCodes.Status201Created);
}
catch
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
If delete FormData and send 'file' get the same error.
If delete 'Content-Type' get 415 status in every case.
If set 'Content-Type' to 'application/json' and IFormFile change to string, then send json it works ok.
1.[FromBody] is used receive application/json data. You need change [FromBody] to [FromForm]
2.To upload files using fetch and FormData.you must not set Content-Type header.
Whole working demo below:
let data = new FormData();
data.append('file', file);
const response = fetch('https://localhost:7054/Pictures',
{
method: 'POST',
body: data
});
Api controller:
[HttpPost]
public async Task<ActionResult> Index([FromForm] IFormFile file)
{
//.....
}

HttpParams set null for call

I have a problem with HttpParams and HttpHeaders after migrating my project from Angular 7 to Angular 8. When I call the API the params are not added. If anyone can help me fix this problem it will be great.
Here is the method in which I define the headers as well as the params.
fetchJson(url: string, parameters ? : any) {
this.token = this.cookieService.get('access_token');
this.contrat_token = this.cookieService.get('contrat_token');
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'application/json');
headers = headers.append('Authorization', 'Bearer ' + this.token);
headers = headers.append('contrat_token', this.contrat_token);
let params = new HttpParams()
params.set('search', parameters);
console.log('les headers');
console.log(headers);
console.log('params');
console.log(params.toString())
return this._http.get(url, {
headers,
params
}).pipe(map((resp: any) => {
if (resp.status === 401 || resp.status == 401 || resp.status.toString() == "401") {
this.clearCookie();
} else {
let reponse = resp;
if (reponse == -1 || reponse == "-1") {
this.router.navigate(["/"]);
}
}
return resp;
}
And I call this method in my services as follows.
getDetailThematiquePrevNext(id: string, typeBase: string) {
let URL = this.urlDecorator.urlAPIDecorate("DI", "GetDetailThematiqueHeaderPrevNext");
let params = this.urlDecorator.generateParameters({
id: id,
typeBase: typeBase,
});
return this.apiFetcher.fetchJson(URL, params);
}
Reason provided by Cue is correct, You need to use chaining or do what you did for headers
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'application/json');
headers = headers.append('Authorization', 'Bearer ' + this.token);
headers = headers.append('contrat_token', this.contrat_token);
let params = new HttpParams()
params = params = params.set('search', parameters);
More readable way to write this would be as follows
const headers = new HttpHeaders()
.append('Content-Type', 'application/json')
.append('Authorization', 'Bearer ' + this.token)
.append('contrat_token', this.contrat_token);
const params = new HttpParams().set('search', parameters);
Also, you can drop Content-Type header, as it is json by default
Probably due to lazy parsing. You have to do a get or getAll to access values to determine the state.
HttpParams class represents serialized parameters, per the MIME type application/x-www-form-urlencoded. The class is immutable and all mutation operations return a new instance.
HttpHeaders class represents the header configuration options for an HTTP request. Instances should be assumed immutable with lazy parsing.
You may want to pass your options directly into the instance for both headers and params:
let headers = new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.token,
'contrat_token': this.contrat_token
});
let params = new HttpParams({
search: parameters
});
As #Siraj stated in an answer, there are alternative ways to set values for headers and params such as set...
let headers = new HttpHeaders().set('name', 'value');
let params = new HttpParams().set('name', 'value');
Or append...
let headers = new HttpHeaders().append('name', 'value');
let params = new HttpParams().append('name', 'value');
The important thing to note here is that these methods require chaining otherwise each method creates a new instance.
You could also convert objects like so:
let headerOptions = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.token,
'contrat_token': this.contrat_token
}
let headers = new HttpHeaders();
Object.keys(headerOptions).forEach((key) => {
headers = headers.set(key, headerOptions[key]);
});
It's also worth avoiding any binding of objects by reference, and instead pass as parameters:
return this._http.get(url, {
headers: headers,
params: params
});
And finally, because your type annotation is "any" for the parameters argument, params expects HttpParamsOptions which is a key/value object where values must be a string annotation.
let params = new HttpParams({
search: JSON.stringify(parameters)
});
Try console.log(params.getAll('search')) but, to make sure headers and params are sent, a better place to check will be Network tab in DevTools.

React native 422 Unprocessable Entity error returned from server when using POST Fetch call with content type: application/x-www-form-urlencoded

I'm calling a simple login API with POST request following are the params:
Headers:
Content-type: application/x-www-form-urlencoded
Body:
email: String
password
Error returned from server is:422 Unprocessable Entity
CODE:
var formBody = new FormData();
formBody.set("email", "test5#gmail.com");
formBody.set("password", "12345678");
const data = new URLSearchParams(new FormData(details));
return dispatch => {
dispatch(requestData());
try {
fetch(`${BASE_URL}users/sign_in`, {
method: 'POST',
// headers: Interceptor.getHeaders(),
headers: {
Accept:'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
// body: formBody
body: data
})
.then(res => res.json())
.then(result=>
{
if (result.success === false) {}
}
)
} catch (error) {
console.log('error',error)
dispatch(failureData(error))
}
}
Screenshot of code
Got the answer, 422 is basically caused by semantic issue, in my case, Origin of my Request Header was going null.

Uploading File to specific folder in Google drive in Angular 4

I am trying to upload a file to a specific folder, but I am unable to do it.
Uploading is working correctly but it is not putting a file in a particular folder.
I am trying to do a Resumable upload with google drive rest version 3.
Assuming I already have a Folder ID.
First getting uploading URI :
uploadFileToDrive(name: string, content: string): Promise<Object> {
const url = `https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable`;
const accessToken = localStorage.getItem('accessToken');
let headers = new Headers({
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + accessToken,
});
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http
.post(url, { name: name, role: 'reader', type: 'anyone', 'parents': [{"id":parentId}] }, options)
.toPromise()
.then(response => this.gDriveUploadFile(content, response.headers.get('location')));
}
Second uploading media :
gDriveUploadFile(file, url): Promise<any> { //file and url we got from first func
console.log(file + " "+ url );
const accessToken = localStorage.getItem('accessToken');
let headers = new Headers({
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json; charset=UTF-8',
'X-Upload-Content-Type': file.type ,
});
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http.post(`${url}`, file, options) //call proper resumable upload endpoint and pass just file as body
.toPromise()
}
Also, I would like to know how I will be able to create a folder using google rest API in angular.

Send data in a http post in angular 2?

I'm trying to send data with http post following differents threads, but I can't do it.
I need to send this data, tested in postman.
Headers.
Content-Type: application/x-www-form-urlencoded
Authorization: Basic user:pass
Body.
grant_type: password
scope: profile
This is my code.
login() {
let url = URL_LOGIN;
let headers = new Headers(
{
'Content-Type': 'application/json',
'Authorization': 'Basic user:pass'
});
let body = {
'grant_type': 'password',
'scope': 'profile'
}
return this.http.post(url, body, { headers: headers })
.map((response: Response) => {
var result = response.json();
return result;
})
}
Thanks in advance!!
There are two things you need to modify:
Your headers passed into the http post method missed one step. It should contain the following:
let options = new RequestOptions({ headers: headers });
Ensure you import RequestOptions from #angular/http
Then pass options into your post method as follows:
return this.http.post(url, body, options)...
The http post method body can only be a string. Therefore, it should be as follows:
let body = 'grant_type=password' + '&scope=profile';

Categories