Why is PUT throwing an Error 400 in my WebAPI? - asp.net-core-webapi

First time working with web APIs and I can't seem to make the PUT method work, while everything works fine (get, post and delete).
I'm trying to do, what I think it is, a very simple call to update testimonials. These will have an image file, name and text. The problem is I'm getting error 400 when trying to update them using a PUT call.
I'm using FormData to send the data since I need to also send the image file and the rest of the form (the URI is "/api/testimonials", and the Id is a guid):
function updateItem() {
const itemId = document.getElementById('edit-id').value;
const item = {
id: itemId,
name: document.getElementById('edit-name').value.trim(),
text: document.getElementById('edit-text').value.trim(),
};
const formData = new FormData();
formData.append('file', document.getElementById('edit-file').files[0]);
formData.append('jsonString', JSON.stringify(item));
fetch(`${uri}/${itemId}`, {
method: 'PUT',
contentType: 'false',
processData: 'false',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: formData
})
.then(() => getItems())
.catch(error => console.error('Unable to update item.', error));
closeInput();
return false;
}
and this is the method in the TestimonialsController:
[HttpPut("{id}")]
public async Task<IActionResult> PutTestimonial(string id, [FromForm] IFormFile? file, [FromForm] string jsonString)
{
...
}
Other methods like POST and Delete, that also pass an Id or data work just fine, but PUT is not being called.
POST method
[HttpPost]
public async Task<ActionResult<TestimonialDTO>> PostDepoimento([FromForm] IFormFile? arquivo, [FromForm] string jsonString)
{
...
}
function addItem() {
const addNameTextbox = document.getElementById('add-name');
const addTextTextbox = document.getElementById('add-text');
const item = {
name: addNameTextbox.value.trim(),
text: addTextTextbox.value.trim(),
};
const formData = new FormData();
formData.append('file', document.getElementById('add-file').files[0]);
formData.append('jsonString', JSON.stringify(item));
fetch(uri, {
method: 'POST',
contentType: 'false',
processData: 'false',
body: formData
})
.then(response => response.json())
.then(() => {
getItems();
addNameTextbox.value = '';
addTextTextbox.value = '';
})
.catch(error => console.error('Unable to add item.', error));
}
DELETE method
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTestimonial(string id)
{
...
}
function deleteItem(id) {
fetch(`${uri}/${id}`, {
method: 'DELETE'
})
.then(() => getItems())
.catch(error => console.error('Unable to delete item.', error));
}
Could you help me identify why is the PUT method not being called? Is there anything wrong in the way I'm calling the method/passing data?

400 error is because [FromForm] does not allow application/json Content-Type. Just remove the header in your updateItem function:
headers: {
'Accept': 'application/json',
//'Content-Type': 'application/json'
},
FromForm occurs at the parameter binding stage, which is part of the request pipeline that necessarily occurs before entry into the endpoint. This is why under debug, for example, your breakpoints wouldn't hit.

Related

use state to build url query string

I am new to Redux, so any help would be appreciated.
I want to add a variable to my fetch GET request URL inside the action creator.
yourapi.com/getuser/{user1}
I might not be following the correct process, I am very new to redux. I am using NextJS with React-Redux for this project.
My action:
// Get User Object
export const load_user = () => async dispatch => {
try {
const res = await fetch(`/api/getuser`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
});
const data = await res.json();
if (res.status === 200) {
dispatch({
type: LOAD_USER_SUCCESS,
payload: data
});
} else {
dispatch({
type: LOAD_USER_FAIL
});
}
} catch(err) {
dispatch({
type: LOAD_USER_FAIL
});
}
};
That part seems ok.
In this getuser.js file, the action calls (The action creator) how do I append a username variable onto the URL ${API_URL}/GetUser/{username} ?
export default async (req, res) => {
if (req.method === 'GET') {
const username = ??????????
try {
// How to get username???
const apiRes = await fetch(`${API_URL}/GetUser/username`, {
method: 'GET',
headers: {
'Accept': 'application/json',
}
});
const data = await apiRes.json();
if (apiRes.status === 200) {
return res.status(200).json({
user: data
});
} else {
return res.status(apiRes.status).json({
error: data.error
});
}
} catch(err) {
return res.status(500).json({
error: 'Something went wrong when retrieving user'
});
}
} else {
// Error. Not a GET request. They tried POST or PUT etc.
res.setHeader('Allow', ['GET']);
return res.status(405).json({
error: `Method ${req.method} not allowed`
});
}
};
I tried
const user = useSelector(state => state.user)
but I get the error
Invalid hook call error - TypeError: Cannot read properties of null (reading 'useContext')

Passing data to Web Service getting - 500 (Internal Server Error)

I have an issue with fetching and passing data to ASP.NET web service from ReactJS.
Other fetch function is working when only getting data from the database.
Why am I getting these errors ? what am I doing here wrong ?
I believe its something in my signUp fetch function.
That response I think is that the fetch can't read the response from the server -
main.chunk.js:2417 POST http://localhost:50496/WebService.asmx/SignUp 500 (Internal Server Error)
SyntaxError: Unexpected token T in JSON at position 0 at JSON.parse (<anonymous>)
ReactJS
userData = {
fName: 'some',
lName: 'two one',
email: 'someonw#gmail.com',
password: 'se123456' }
My fetch function in ReactJs -
const signUp = (signUpData) => {
let data = {
firstName: signUpData.fName,
lastName: signUpData.lName,
emailAddress: signUpData.email,
password: signUpData.password,
};
fetch('http://localhost:50496/WebService.asmx/SignUp', {
body: JSON.stringify(data),
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
})
.then((response) => response.text())
.then(
(xml) =>
new window.DOMParser().parseFromString(xml, 'text/xml')
.documentElement.firstChild.textContent
)
.then((jsonStr) => JSON.parse(jsonStr))
.then((userData) => {
if (userData.d && userData.d.length > 0) {
setUser(userData);
} else {
console.log('error no data');
}
})
.catch((err) => {
console.log(err);
});
};
Error from Chrome -
System.InvalidOperationException: Missing parameter : firstName.
System.Web.Services.Protocols.ValueCollectionParameterReader.Read(NameValueCollection collection)
System.Web.Services.Protocols.UrlParameterReader.Read(HttpRequest request)
System.Web.Services.Protocols.HttpServerProtocol.ReadParameters()
System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()
SignUp WebMethod is working in the WebService.
[WebMethod]
public string SignUp(string firstName, string lastName, string emailAddress, string password)
{
return BLL.SignUp(firstName, lastName, emailAddress, password);
}
I found What I needed to do,
another error was -
InvalidOperationException: Only web services with the [ScriptService]
attribute in the class definition can be read from a script.
So I added to WebService.cs the Attribute Class[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
And Since this completely eliminates the need to parse XML as you must when using standard SOAP (Simple Object Access Protocol) web services, I removed this code lines -
.then((response) => response.text())
.then(
(xml) =>
new window.DOMParser().parseFromString(xml, 'text/xml')
.documentElement.firstChild.textContent
)
.then((jsonStr) => JSON.parse(jsonStr))
And changed to this -
.then((response) => response.json())
.then((userData) => {
if (userData.d && userData.d.length > 0) {
let user = JSON.parse(userData.d);
setUserData(user);
}

javascript post fetch request gone wrong

please, help me check this post request.
I've been looking at it since yesterday, I don't know what is wrong with it
maybe I need another developer's pairs of eyes.
thanks in advance
buttons.addEventListener('click', ()=>{
fetch("https://jsonplaceholder.typicode.com/comments", config)
.then(res=>{
res.json();
}).then(datae=>{
console.log(datae);
})
});
const config = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newName)
}
buttons.addEventListener('click', () => {
fetch('https://jsonplaceholder.typicode.com/comments', config)
.then((res) => {
return res.json()
})
.then((datae) => {
console.log(datae)
})
})
you just need to return res.json()
this should work, but remember that the function you pass to the .then function has access to the returned value from the previous .then.
fetch returns a promise that resolves into an object with the .json function
so in the first .then it will get that object and you need to return res.json() which returns a promise that will be resolved to the JSON data
so in the next .then you can use that data
I hope I was clear
note:
.then function returns a promise ( always )
also maybe you have an error in the config variable, what you pass to the JSON.stringify function should be a valid javascript object
const config = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'rowadz' }),
}

Angular2 response not mapping to TypeScript object

I'm struggling to figure out why the response I get from my API isn't mapping to an object that I have in typescript.
Here's the function in my service that calls the API:
register(user: IUser): Observable<IUser> {
var headers = new Headers();
headers.append('Content-Type', 'application/json');
var options = new RequestOptions({
headers: headers,
url: this._registerUrl,
body: JSON.stringify(user)
});
return this._http.post(this._registerUrl, { user }, options)
.map((res: Response) => res.json() as IUser)
.catch(this.handleError);
}
This is the function that calls the service:
register(): void {
let user: IUser = {
email: this.email,
username: this.username,
password: this.password
}
this._userService.register(user)
.subscribe(result => {
debugger;
if(result.errorCode > 0)
this.handleError(result.errorCode);
else {
localStorage.setItem('userId', result.userId.toString());
localStorage.setItem('username', result.username.toString());
localStorage.setItem('email', result.email.toString());
}
});
}
The object that I am returning from the API matches the object that I have in the frontend. It is returning the data and I can see it in the body of my response. All of the data is right, but it's in the body and is not turning it into an IUser object.
Does anybody have any ideas? Thanks.
EDIT
This is what the response object looks like when it comes back from the service.

Sending data as key-value pair using fetch polyfill in react-native

The following code is to make HTTP POST request with fetch polyfill:
fetch(url, {
method: 'post',
body: JSON.stringify({
token: this.state.token,
}),
})
.then((response) => response.json())
.then((responseData) => {
console.log(responseData);
})
.done();
This request sends data as a stringified JSON obj. Is there a way to send data as key-value pair similar to requests? post(URL, data=payload) in python.
Sounds like you want the same format as a querystring, so import/require a package like https://www.npmjs.com/package/query-string which doesn't appear to depend on any browser features and has a stringify method:
queryString.stringify({
foo: 'bar',
nested: JSON.stringify({
unicorn: 'cake',
}),
});
//=> foo=bar&nested=%7B%22unicorn%22%3A%22cake%22%7D
Alternatively you could just use the relevant part of its source code, though this would still be subject to its license:
function toQueryString(obj) {
return obj
? Object.keys(obj)
.sort()
.map(function (key) {
var val = obj[key];
if (Array.isArray(val)) {
return val
.sort()
.map(function (val2) {
return encodeURIComponent(key) + '=' + encodeURIComponent(val2);
})
.join('&');
}
return encodeURIComponent(key) + '=' + encodeURIComponent(val);
})
.join('&')
: '';
}
You can then use the return value in your body parameter in fetch:
fetch(url, {
method: 'post',
body: toQueryString({ token: this.state.token }),
});
Sure. Look at the fetch documentation in github: https://github.com/github/fetch
It uses document/DOM web, but it should be the same for react-native case - just use FormData object and append all the form fields to send.
var form = document.querySelector('form')
fetch('/users', {
method: 'post',
body: new FormData(form)
})
And:
var input = document.querySelector('input[type="file"]')
var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')
fetch('/avatars', {
method: 'post',
body: data
})

Resources