How to make a POST request with httpclient in net core and javascript - entity-framework

Im having a very bad time triying to figure out this problem, I have a web API made in net core 6 with Entity Framework, in this web api I have to consume a third party API. If a try to make a POST request directly the Swagger UI it works perfectly:
POST in Swagger
HOWEVER, if i made a post request in javascript using fetch it return a 400 error, that to be honest doesn't say much:
400 error response
I know there is no missing data in my post request, I checked a lot, in fact there is no field call "data".
Here is the code to make a fetch in the frontend:
return fetch(apiURL, {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: JSON.stringify(data)
})
.then(response => response.json())
Here is the post method in net core
[HttpPost]
public async Task<string> PostProductAsync(string data)
{
HttpResponseMessage response = await _client.PostAsync(path, new StringContent(data, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return json;
}
I'm a bit confused. What could i be doing wrong.
UPDATE
I followed each instruction in the answers section, and I can say that it makes a lot of sense to put [FromBody] in the POST method since in the fetch method I am sending the data in the body.
However this led me to a new parsing error: parsing error after POST request
I have been searching in other forums about this error, it seems that what is recommended in this case is to make a class Unexpected character encountered while parsing value: . Path '', line 1, position 1
Now, the problem that i have with this approach its that i have quite a big json to post, witch means that i will have to create a lot of classes to make this possible. Its there any way to this without creating a class?
So far i tried the following changes in the POST method:
Adding [FromBody]
[HttpPost]
public async Task<string> PostProductAsync([FromBody] string data)
{
HttpResponseMessage response = await _client.PostAsync(path, new StringContent(data, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return json;
}
This one leads to the parsing error mentioned before.
Adding [FromBody] and changing string to object
[HttpPost]
public async Task<string> PostProductAsync([FromBody] object data)
{
HttpResponseMessage response = await _client.PostAsync(path, new StringContent(data.ToString(), Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return json;
}
This one leads to a non valid JSON is not a valid JSON error

Your Swagger UI is sending your data as a Query parameter, but you are trying to post it through body in your fetch. They are two different methods of post. I recommend you use body only.
Change your controller method to use the [FromBody] parameter attribute:
PostProductAsync([FromBody] string data)
More information from https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api#using-frombody
Defaults for a string, in your case:
If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string. (More about type converters later.)
So the default behavior is what Swagger is currently using.

Since you want to pass json type data,you need to use 'Content-Type': 'application/json' in fetch,and then use [FromBody]in action.
fetch:
return fetch(apiURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => response.json())
action:
[HttpPost]
public async Task<string> PostProductAsync([FromBody]string data)
{
HttpResponseMessage response = await _client.PostAsync(path, new StringContent(data, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return json;
}

In your case, you can bind model in two ways.
Simple type model binding approach
Since you are accepting simple type string value in controller, you can use 'Content-Type': 'application/x-www-form-urlencoded' in fetch and you can pass parameter data in the URL as following:
At fetch:
let apiURL = `https://localhos:8080/Test/data`;
return fetch(apiURL, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: null
}).then(response => response.json())
Complex type model binding approach
Let's assume you're now posting complex object type data. To post data from request body Content-Type should be application/json. And add [FromBody] in the action to define the location to get data from request. Here's the example code snippet.
At fetch:
return fetch(apiURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}).then(response => response.json())
At controller:
[HttpPost]
public async Task<string> PostProductAsync([FromBody]string data)
{
HttpResponseMessage response = await _client.PostAsync(path, new StringContent(data, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return json;
}
On a side note, please avoid using text/plain as Content-Type to avoid security issues.
Hope this helps you and here's for your further reference for model binding in ASP.NET Core.

Related

Dart TypeError : type 'JSString' is not a subtype of type 'int' for a Http POST request

I'm building an application using flutter where the user provides a string and a set of values must be returned.
I'm unable to figure out what is the cause for the issue.I tried all the solutions provided to the questions similar to this issue but weren't successful.Any help would be really appreciated.
I converted the actual code to dart only, for easy testing online using dartpad.
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
final body = <String, String>{
"id": '1',
"language": "en",
"text": "I love this service",
};
final headers = <String, String>{
"content-type": "application/json",
"X-RapidAPI-Key": "7f980b3d2cmsh1d666b571febd6ep11df80jsna27f76c06e6b",
"X-RapidAPI-Host": "big-five-personality-insights.p.rapidapi.com",
};
void main(List<String> arguments) async {
final response = await http.post(
Uri.parse('https://big-five-personality-insights.p.rapidapi.com/api/big5'),
headers: headers,
body: [
convert.jsonEncode(body),
],
);
if (response.statusCode == 201) {
// If the server did return a 201 CREATED response,
// then parse the JSON.
print('success');
print(convert.jsonDecode(response.body));
} else {
// If the server did not return a 201 CREATED response,
// then throw an exception.
print('fail');
throw Exception('Failed to get a response.');
}
}
You have a bad value for the body argument of http.post.
The documentation for the method states:
body sets the body of the request. It can be a String, a List or a Map<String, String>. [...] If body is a List, it's used as a list of bytes for the body of the request.
Since the API you are talking to requires an array to be sent, you want to wrap the body in a list before converting it all to json (note how the brackets have shifted inside the convert method:
final response = await http.post(
Uri.parse('https://big-five-personality-insights.p.rapidapi.com/api/big5'),
headers: headers,
body: convert.jsonEncode([body]),
);
Sidenote: The API responds with statusCode 200 on a successful request, not 201; at least in my testing.
The body parameter of a post method sets the body of the request. It can be a String, a List or a Map<String, String>. If it's a String, it's encoded using encoding and used as the body of the request. The content-type of the request will default to "text/plain".
As you passed it as a List, then it expects it to be a List of integers, but you are passing it a List type (or in this specific case List type). Here is a fixed code.
final response = await http.post(
Uri.parse('https://big-five-personality-insights.p.rapidapi.com/api/big5'),
headers: headers,
body: convert.jsonEncode(body),
);

Converting my Postman Call to Dart/Flutter API Call

I hope to use nutritionix api to get food information for the users of my application, I manage to get the call to work in Postman, however I cannot convert it to dart code. I am getting this error: '{message: Unexpected token " in JSON at position 0}'
Here is my (POST) postman call:
Here is my attempt at converting that to dart code:
Future<void> fetchNutritionix() async {
String url = 'https://trackapi.nutritionix.com/v2/natural/nutrients';
Map<String, String> headers = {
"Content-Type": "application/json",
"x-app-id": "5bf----",
"x-app-key": "c3c528f3a0c68-------------",
"x-remote-user-id": "0",
};
String query = 'query: chicken noodle soup';
http.Response response =
await http.post(url, headers: headers, body: query);
int statusCode = response.statusCode;
print('This is the statuscode: $statusCode');
final responseJson = json.decode(response.body);
print(responseJson);
//print('This is the API response: $responseJson');
}
Any help would be appreciated! And, again thank you!
Your postman screenshot shows x-www-form-urlencoded as the content-type, so why are you changing that to application/json in your headers? Remove the content type header (the package will add it for you) and simply pass a map to the body parameter:
var response = await http.post(
url,
headers: headers,
body: {
'query': 'chicken soup',
'brand': 'acme',
},
);
Also you can now generate Dart code (and many other languages) for your Postman request by clicking the Code button just below the Save button.
click the three dotes button in request tab and select code option then select your language that you want convert code to
review the query you're posting
your Postman input is x-www-form-urlencoded instead of plain text
String query = 'query: chicken noodle soup';
why don't you try JSON better
String query = '{ "query" : "chicken noodle soup" }';

Dart HttpClient not reading full response

I have a weird issue using the dart HttpClient where it seems like it's not reading the full response before "finishing". This is the code I have:
HttpClient client = new HttpClient();
client.connectionTimeout = Duration(seconds: 60);
Completer<MealPlan> completer = new Completer();
RecipeSearchFilter filter = new RecipeSearchFilter();
filter.restrictions = intolerances.map((intolerance) => intolerance.name).toList();
MealPlan mealPlan;
client
.getUrl(Uri.parse(
"https://myUrl.com/api/meal-plans?filter=${jsonEncode(filter)}"))
.then((HttpClientRequest request) {
request.headers.set("Authorization", "Bearer $idToken");
return request.close();
}).then((HttpClientResponse response) {
response.transform(utf8.decoder).listen((contents) {
Map<String, dynamic> contentMap = jsonDecode(contents);
mealPlan = MealPlan.fromJson(contentMap);
}, onDone: () => completer.complete(mealPlan));
});
return completer.future;
This is the most intensive function that my app contains, so this particular API itself typically takes 6-8 seconds to fully complete since there's a lot going on behind the scenes. The response isn't big (~60KB). Making the exact same call using Postman I can see that it does indeed return everything as expected, but if I look at the response inside of the }).then((HttpClientResponse response) { block, it only contains a very small, partial bit of the response. I'm not sure what I'm doing wrong here, but I'm guessing that I've configured the HttpClient incorrectly.
The problem is that your response will be delivered by the stream in pieces. After passing those chunks through the utf8 decoder, you should then form them into a single string before trying to json decode it. Currently you try to json decode the first chunk without waiting for the rest.
It's much easier to use the package:http which is a wrapper around HttpClient, and does a lot of the grunt work for you. Also, it's more readable to use the async/await syntax rather than .then.
Using an async method you could write, for example:
void getPlan(Map filter, String idToken) async {
var response = await http.get(
Uri.parse('https://myUrl.com/api/meal-plans?filter=${jsonEncode(filter)}'),
headers: <String, String>{
'Authorization': 'Bearer $idToken',
},
);
return MealPlan.fromJson(json.decode(response.body));
}
If you really want to control the connection timeout, you need to pass in your own HttpClient, as follows:
var client =
http.IOClient(HttpClient()..connectionTimeout = Duration(seconds: 60));
var response = await client.get(
//etc

Flutter http Maintain PHP session

I'm new to flutter. Basically I'm using code Igniter framework for my web application. I created REST API for my web app, after user login using API all the methods check for the session_id if it exists then it proceeds, and if it doesn't then it gives
{ ['status'] = false, ['message'] = 'unauthorized access' }
I'm creating app with flutter, when i use the http method of flutter it changes session on each request. I mean, it doesn't maintain the session. I think it destroys and creates new connection each time. Here is thr class method which i use for api calls get and post request.
class ApiCall {
static Map data;
static List keys;
static Future<Map> getData(url) async {
http.Response response = await http.get(url);
Map body = JSON.decode(response.body);
data = body;
return body;
}
static Future postData(url, data) async {
Map result;
http.Response response = await http.post(url, body: data).then((response) {
result = JSON.decode(response.body);
}).catchError((error) => print(error.toString()));
data = result;
keys = result.keys.toList();
return result;
}
I want to make API request and then store session_id,
And is it possible to maintain session on the server so i can manage authentication on the web app it self.?
HTTP is a stateless protocol, so servers need some way to identify clients on the second, third and subsequent requests they make to the server. In your case you might authenticate using the first request, so you want the server to remember you on subsequent requests, so that it knows you are already authenticated. A common way to do this is with cookies.
Igniter sends a cookie with the session id. You need to gather this from each response and send it back in the next request. (Servers sometimes change the session id (to reduce things like clickjacking that we don't need to consider yet), so you need to keep extracting the cookie from every response.)
The cookie arrives as an HTTP response header called set-cookie (there may be more than one, though hopefully not for simplicity). To send the cookie back you add a HTTP request header to your subsequent requests called cookie, copying across some of the information you extracted from the set-cookie header.
Hopefully, Igniter only sends one set-cookie header, but for debugging purposes you may find it useful to print them all by using response.headers.forEach((a, b) => print('$a: $b'));. You should find Set-Cookie: somename=abcdef; optional stuff. We need to extract the string up to, but excluding the ;, i.e. somename=abcdef
On the next, and subsequent requests, add a request header to your next request of {'Cookie': 'somename=abcdef'}, by changing your post command to:
http.post(url, body: data, headers:{'Cookie': cookie})
Incidentally, I think you have a mismatch of awaits and thens in your code above. Generally, you don't want statics in classes, if they should be top level functions instead. Instead you could create a cookie aware class like:
class Session {
Map<String, String> headers = {};
Future<Map> get(String url) async {
http.Response response = await http.get(url, headers: headers);
updateCookie(response);
return json.decode(response.body);
}
Future<Map> post(String url, dynamic data) async {
http.Response response = await http.post(url, body: data, headers: headers);
updateCookie(response);
return json.decode(response.body);
}
void updateCookie(http.Response response) {
String rawCookie = response.headers['set-cookie'];
if (rawCookie != null) {
int index = rawCookie.indexOf(';');
headers['cookie'] =
(index == -1) ? rawCookie : rawCookie.substring(0, index);
}
}
}

Dart : i can't POST data in HTTP Message Body using HttpRequest.send() method

In my Rest server, i can't read the data contained in the HTTP Message Body when the call comme from script writing in Dart.
When calling the same webservice with ajax, there is no problem. In both case the data send with the URL (pcParamUrl=yopla) can be read in the server side.
i think the problem come from "processData: false" in ajax, i don't know how to set this key in HttpRequest Dart object.
I am trying to convert this ajax call (javascript) :
var url = "http://127.0.0.1:8980/TestDartService/rest/TestDartService/Test?pcParamUrl=yopla";
$.ajax({url: url,
type: "POST",
processData: false,
contentType: "application/json",
data: JSON.stringify({ request: {pcParamBody: "yepYep"} }),
success: function(data, status) { alert(data.response.pcRetour); },
error: function(status) { alert("erreur"); }
});
to this one using Dart:
HttpRequest request = new HttpRequest();
request.onReadyStateChange.listen((_) {
if (request.readyState == HttpRequest.DONE && request.status == 200 || request.status == 0)) {
print(request.responseText);
}
});
var url = "http://127.0.0.1:8980/TestDartService/rest/TestDartService/Test?pcParamUrl=yopla";
request.open("POST", url, async: false);
request.setRequestHeader("content-Type", "application/json");
String jsonData = JSON.encode('{ request: { pcParamBody: "yepYep" } }'); // etc...
request.send(jsonData);
thanks for your help and sorry for my bad english
(moved from the Question:)
The problem is the format of the data being passed to JSON.encode; it should be an object; or you could skip the encoding altogether:
String jsonData = JSON.encode('{request: {pcParamBody: "yepYep"}}'); // BAD
String jsonData = JSON.encode({"request": {"pcParamBody": "yepYep"}}'); // GOOD
String jsonData = '{request: {pcParamBody: "yepYep"}}'; // ALSO GOOD