Axios Adds Brackets [] To Request Param - reactjs

I'm trying to add a list of items as a request parameter. When I manually write the URL like that: http://localhost:8080/api/user/book/?keyword=&page=1&pageSize=5&field=id&order=ascend&author=aziz&author=nazim, I get what I want.
However, Axios insists on adding square brackets pointlessly.
const _fetchBooks = async (params) => {
const url = `${UrlUtil.userURL()}/book/`;
const response = await axios.get(url, {
params: {
keyword: params.search,
page: params.pagination.current,
pageSize: params.pagination.pageSize,
field: params.sorter?.field,
order: params.sorter?.order,
author: params.filter?.author,
},
...
};
Here's the result URL React trying to reach when I do filtering:
http://localhost:8080/api/user/book/?keyword=&page=1&pageSize=5&field=id&order=ascend&author[]=aziz&author[]=nazim
With the brackets, I cannot go on because I get an exception:
java.lang.IllegalArgumentException: Invalid character found in the request target [/api/user/book/keyword=&page=1&pageSize=5&field=id&order=ascend&author[]=aziz&author[]=nazim ]. The valid characters are defined in RFC 7230 and RFC 3986
So, Java doesn't like the brackets.

As #AndyRay said in the comment, it's not React doing this, its Axios. This is standard behaviour as documented by their API. You can change the way that arrays get serialized using paramsSerializer option:
const response = await axios.get(url, {
params: {
keyword: params.search,
page: params.pagination.current,
pageSize: params.pagination.pageSize,
field: params.sorter?.field,
order: params.sorter?.order,
author: params.filter?.author,
},
paramsSerializer: { indexes: null }
});

as I can understand your issue of Invalid character found
I suggest you to read this first:
What's valid and what's not in a URI query?
To solve this issue of using invalid characters such as "[" you need to encode the url params and to do so check this fuction:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
Example:
const _fetchBooks = async (params) => {
const url = `${UrlUtil.userURL()}/book/`;
const response = await axios.get(url, {
params: {
keyword: params.search,
page: params.pagination.current,
pageSize: params.pagination.pageSize,
field: params.sorter?.field,
order: params.sorter?.order,
author: encodeURIComponent(JSON.stringify(params.filter?.author)),
},
...
};
Best of luck ...

Related

How to upload images in react via api?

Today I saw a number of tutorials on how to upload photos in react via the api.
I did everything, tried all the methods. But in the end I get stuck.
(During the whole explanation I will focus only on the features of the image upload)
In Models I have groups and variable -
[NotMapped]
public IFormFile ImageFile {get; set; }
In api I get
[Route ("Add")]
[HttpPost]
public void Post (Group group)
And I have in state-
const initialFieldValues ​​= {
GroupName: '',
GroupAbout: '',
imageName: '',
imageSrc: defaultImageSrc,
imageFile: null
}
const [values, setValues] = useState (initialFieldValues)
When changing the image has a function-
const handleImg = (e) => {
if (e.target.files && e.target.files [0]) {
let imageFile = e.target.files [0];
const reader = new FileReader ();
reader.onload = x => {
setValues ​​({
... values,
imageFile,
imageSrc: x.target.result
})
}
reader.readAsDataURL (imageFile)
SetDisplayImg ("block");
}
else {
setValues ​​({
... values,
imageFile: null,
imageSrc: defaultImageSrc
})
}
};
And when submitting the form
const handleFormSubmit = e => {
e.preventDefault ()
const formData = new FormData ()
.append ('groupImage', values.imageFile)
addOrEdit (formData)
}
const addOrEdit = (formData) => {
axios.post ('api / groups / add', formData) .catch (error => {
console.log (error.response.data);
console.log (error.response.status);
console.log (error.response.headers);
});
}
In this code -makes error 415 (even regardless of uploading the image but, even if I put it only other variables that get stringed and work normally.)
If I add [FromForm] in the api it does not respond to me, i.e. it does not write me an error message nor does it reach the api (I checked in debugging)
If I change the axios to
const obj = {'groupImage': values.imageFile
}
axios.post ('api / groups / add', obj) .catch (error =>
I get an error message 400-
"The JSON value could not be converted to System.String. Path: $ .groupImage
And if I send the value from state
axios.post ('api / groups / add', values)
I get an error message System.NotSupportedException: Deserialization of interface types is not supported. Type 'Microsoft.AspNetCore.Http.IFormFile'. Path: $ .imageFile | LineNumber: 0 | BytePositionInLine: 6939781.
---> System.NotSupportedException: Deserialization of interface types is not supported. Type 'Microsoft.AspNetCore.Http.IFormFile'.
Anything I try to fix, it causes another error, I'm really at a loss.
Regards
>.append ('groupImage', values.imageFile)
Firstly, please make sure the key of your formdata object can match your model class property's name.
formData.append('imageName', values.imageName);
//...
//it should be imageFile, not groupImage
formData.append('imageFile', values.imageFile);
Besides, please apply the [FromForm] attribute to action parameter, like below.
public void Post([FromForm]Group group)
{
//...
Test Result
Usually a 415 means you aren't setting the right Content-Type header. Does the API you are trying to upload to mention acceptable types or encodings it expects?

How to post data when you have javascript object using multipart/form-data content-type

So I have never post a data using FormData and multipart/form-data as Content-Type in my React project. But now I'm kinda forced by backend to send it this way since we have some images in payload.
The problem is that the whole data is a JS object and can be parsed to JSON, and nothing more. So how can I convert my JS object into a valid FormData that backend would accept it? Everything works without a problem in Postman, but in the app I always get the same error.
Here is some more detail:
The working Postman sample:
What I expect to be working (but obviously doesn't):
const createProd = () =>
HttpRequest.post('/api/prod', {
body: {
Product: {
title: 'Test Prod',
shop: null,
description: "My new ad's description",
category: { id: '5d8c6aa6fadeaf26b0194667' },
condition: 'USED'
}
});
HttpRequest is a helper function which uses ES6 fetch for requests.
I always get the same error: "Required request part 'Product' is not present" with/without JSON.stringify.
I even tried to create a sample FormData to at least change the error:
cont payload = new FormData();
payload.append('Product', {foo: 'bar'});
But still same error. I also copied the code which is generated by Postman. But still no chance.
I would be thankful if you share your suggestions or workarounds.
Thanks in advance.
const formData = new FormData();
const product = { //your product object };
formData.append('product', JSON.stringify(product));
const config = {
headers: {
'Content-Type': 'multipart/form-data; charset=utf-8; boundary="another cool boundary";'
}
};
axios.post(`/api/ads`, formData, config).then((res) => {
console.log(res);
}).catch(err => {
console.log(err);
});
Maybe you should set header.
Try this one. In my case I used Axios. It worked for me in my project.

Passing through an array with axios

I am having an issue passing through an array through axios post call. The issue is that on the api endpoint the data received is null, when I try posting using postman it works fine so the endpoint is working. Example of the array
I need to pass the data in this format:
{
"UpdateItemList": [
{
"Text": 1,
"Value": "5"
},
{
"Text": 1,
"Value": "5"
}
]
}
Code:
export function createLogEntry(postData) {
let payload = {
UpdateItemList: postData
};
const request = axios.post('https://localhost:44312/api/Audit/AddLogEntry', {
data: payload
});
return {
type: CREATE_LOG,
payload: request
}
}
Is there any issue with the way I am passing through the data with my current code?
Try with
const request = axios.post('https://localhost:44312/api/Audit/AddLogEntry',payload);
This worked for me!
The issue is that you are confusing two ways axios can be used. Currently you are actually posting your data nested in an object within and the key data:
{
data: {
UpdateItemList: postData
}
}
If you are using the axios.post function, you should just pass your object with the data to post as the second object like this:
const request = axios.post('https://localhost:44312/api/Audit/AddLogEntry', payload);
If you are using the config object method, you should just pass one single object with url, method and data as keys.
// Send a POST request
axios({
method: 'post',
url: 'https://localhost:44312/api/Audit/AddLogEntry',
data: payload
});
This behaviour is explained in the axios Readme here: https://github.com/axios/axios#axios-api

React Post not showing Header

Fixed:
I changed a few things in a go, so i can't really tell what solved my issue, but basically:
-the API needed int or long and i was sending String.. parseInt solved it.
I declared initially a data object:
const [data, setData] = useState({
})
With all the values i was gonna send later on(and with the correct data types! if i'm not sure, null seems to work pretty well for initialization).
-Another issue was that i merged all my data in a hook just before my POST request, since its asynchronous..it's a bad idea.
-Lastly, but most importantly, and that was probably the issue (thanks to EntiendoNull and JMadelaine), i initialized my data as [] and not as {}, so i was sending an object inside of an array.. hence the JSON parse error: Cannot deserialize instance of message in the API when i POST.
.----------------------------------------------------------------------------------------------------------------------------------.
I've got an issue with a post request not working properly.
Basically, the header is not right, it should be {"content-type": "application/json"}
But instead, it's an empty object.
Here is where i do the POST:
const [data, setData] = useState([]);
const createMission = async (e) => {
setData({
...data,
nom:state.missionName,
id_client:state.currentClientKey,
id_consultant:state.currentConsultantId,
id_manager:1,
id_ro:state.currentROKey,
id_rc:state.currentRCKey,
detail:state.missionDescription,
adresse:state.adresse,
frequence_eval:6,
date_debut:state.startDate,
date_fin:state.endDate,
metier:state.metier
});
let headers = {
headers: {'Content-Type': 'application/json'}
};
axios.post('http://localhost:8080/mission',data,{"headers" : headers})
.then(res => {
console.log(res);
});
Here is the content of the res on the console of my navigator:
{…}
​
config: Object { url: "http://localhost:8080/mission", method: "post", data: "{\"nom\":\"\",\"id_consultant\":null,\"id_manager\":1,\"detail\":\"\",\"adresse\":\"\",\"frequence_eval\":6,\"date_debut\":null,\"date_fin\":null,\"metier\":null}", … }
​
data: ""
​
headers: Object { }
​
request: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
​
status: 200
​
statusText: ""
​
<prototype>: Object { … }
The data is sent fine, I get a 200 status back, but as you can see, the headers have an empty object in it, and the data is an empty string "".
Funny thing is, I do almost the exact same thing on another component, and it works just fine :
In this case, I don't even specify the headers.
const createClient = async (e) => {
axios.post('http://localhost:8080/client', {nom:state.nomClient})
.then(res => {
console.log(res);
});
};
And here is the log :
{…}
​
config: Object { url: "http://localhost:8080/client", method: "post", data: "{\"nom\":\"aze\"}", … }
​
data: Object { id: 372, nom: "aze" }
​
headers: Object { "content-type": "application/json" }
​
request: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
​
status: 200
​
statusText: ""
​
<prototype>: Object { … }
The API is the same, it's a #RequestBody.
Here for the mission :
#PostMapping
public Mission addMission (#RequestBody MissionBean missionBean){
Optional<Client> optionalClient = clientRepository.findById(missionBean.id_client);
Optional<Consultant> optionalConsultant = consultantRepository.findById(missionBean.id_consultant);
Optional<Utilisateur> optionalManager = utilisateurRepository.findById(missionBean.id_manager);
Optional<Utilisateur> optionalRo = utilisateurRepository.findById(missionBean.id_ro);
Optional<Utilisateur> optionalRc = utilisateurRepository.findById(missionBean.id_rc);
if (optionalClient.isPresent() && optionalManager.isPresent() && optionalConsultant.isPresent() && optionalRo.isPresent() && optionalRc.isPresent()){
Mission newMission = new Mission(
missionBean.nom,
optionalClient.get(),
optionalConsultant.get(),
optionalManager.get(),
optionalRo.get(),
optionalRc.get(),
missionBean.detail,
missionBean.adresse,
missionBean.frequence_eval,
missionBean.date_debut,
missionBean.date_fin,
missionBean.metier
);
missionRepository.save(newMission);
return newMission;
}
return null;
}
and here for the client(2nd example that works just fine) :
#PostMapping
public Client addClient (#RequestBody Client client){
clientRepository.save(client);
Critere critere = new Critere(client,"Ponctualité",true);
Critere critere2 = new Critere(client,"Assiduité",true);
Critere critere3 = new Critere(client,"Intégration",true);
critereRepository.save(critere);
critereRepository.save(critere2);
critereRepository.save(critere3);
return client;
}
I've tried many different ways to specify the headers, but it still doesn't work.
The request works good(200), but the data is not being understood API side, no header, so an empty string as the data, I'm kind of new to this so I'm stuck here. When I check my Database nothing new has been added.
Edit:
I got a message from the API each time i make the request :
2020-02-22 22:19:39.737 WARN 4205 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `com.evalside.project.beans.MissionBean` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `com.evalside.project.beans.MissionBean` out of START_ARRAY token
at [Source: (PushbackInputStream); line: 1, column: 1]]
Maybe this can help pointing the issue
Edit2: Some of my values are supposed to be int but come as String(just noticed) Gonna fix that and try again
You probably do not want to use your state like that.
First, the initial value of your data state should be an object (according do what kind of type and value you point it to be later). Better be clear with your intentions from start :)
const [data, setData] = useState({});
Second, when updating a state the way you do may lead to problems.
Consider using setDate(prevState => { // your logic }) instead.
Third, from the code you are showing it does not make much sense to set your state the way you do and then feed it to your axios.post request. Or is there any other reason why those values are stored in a state before being used in your request? Most of them seem to already be stored in some other state already, right?
Instead, you could have declared a const holding those values:
const myData = {
nom:state.missionName,
id_client:state.currentClientKey,
id_consultant:state.currentConsultantId,
id_manager:1,
id_ro:state.currentROKey,
id_rc:state.currentRCKey,
detail:state.missionDescription,
adresse:state.adresse,
frequence_eval:6,
date_debut:state.startDate,
date_fin:state.endDate,
metier:state.metier
}
Typically once your side effect is done and you have received a response is when you may want to update a state like this one.
Fourth, you did declare a headers-object;
let headers = {
headers: {'Content-Type': 'application/json'}
};
So in your axios-request do this:
axios.post('http://localhost:8080/mission', data, headers);
instead of this:
axios.post('http://localhost:8080/mission',data,{"headers" : headers})
thanks for your help
First, the initial value of your data state should be an object (according do what kind of type and value you point it to be later). Better be clear with your intentions from start :)
const [data, setData] = useState({});
Actually it is, i wrote it like : const [data, setData] = useState([]); not with {}.
I'm starting to get into hooks, so yep i don't have the right habits yet, i'm gonna try to use setDate(prevState => { // your logic }) more often.
I have several components all lifting some values into this one, which will eventually make the post request, that's why it's a bit messy with all the states and the data passing by, but i've tried many ways, and i've already tried simply doing :
const data = {name:myName, date:myDate etc..}
(...)
axios.post('url', data, headers)
I have other things happening i missed in the API, gonna add it to the post, maybe it'll help ;)

How to get data in all the pages of an API http request using observables in Angular?

I am making an http.get request to an API to get data. The API uses pagination and contains 30 entries/page. It also provides information about next page and last page along with respective links (also prev page and first page when applicable) in the response header links.
The Link header includes pagination information:
<https://api.example.com/user?page=2>; rel="next",
<https://api.example.com/user?page=10>; rel="last"
The JSON object in response.body
[
{
userId: 1,
name: 'John',
created_at: "2015-10-13T03:10:43Z",
},
{
userId: 2,
name: 'Jane',
created_at: "2019-02-15T13:37:03Z",
}
....
]
I am using angular 6 and trying to accumulate the data by making subsequent http.get calls. I have tried to use array method push() for subsequent data. But the since the typeof resp.body is object it is not working.
userData: any[];
getData(url: string) {
this.http.get(url, {observe: 'response'});
.subscribe((resp) => {
this.userData.push(resp.body);
}
Error message: TypeError: Cannot read property 'push' of undefined
The userData should contain array of data received from http.get requests which is iterable.
You can't .push to an uninitialized array.
Try doing this:
userData = [];
as mentionned in previous answer you should initialize your array before you can use push() on it.
But if I understand well you receive an array of user for each response. By using push on each response you will obtain an array of array.
You should either use concat to merge the two arrays or use a foreach on the response and then push.
also I would recommend you using interfaces or classes and try to avoid any
interface UserData {
userId: number;
name: string;
created_at: string;
}
userData: UserData[] = [];
getData(url: string) {
this.http.get(url, {observe: 'response'})
.subscribe((resp) => {
resp.body.forEach((user: UserData) => {
this.userData.push(user);
});
});
}
userData: any[] = [];
userData is not initialized which is throwing this error. Try it with empty Array.
Initialize the array at first then push, The following code looks like this.
userData: any[] = [];
getData(url: string) {
this.http.get(url, {
observe: 'response'
});
.subscribe((resp) => {
// this.userData = [];
this.userData.push(resp.body);
}

Resources