How to send a POST request with Content-Type "application/x-www-form-urlencoded" - rest

I am trying to send a POST request from my TornadoFX application to my nodejs server.
That's how i send my request to the server:
val api: Rest by inject()
api.baseURI = "http://localhost:5000/"
api.post("api/register", userModel) {
it.addHeader("Content-Type", "application/x-www-form-urlencoded")
}
My UserModel looks like this:
class UserModel : JsonModel {
val nameProperty = SimpleStringProperty("")
var name by nameProperty
val emailProperty = SimpleStringProperty("")
var email by emailProperty
override fun toJSON(json: JsonBuilder) {
with(json) {
add("name", name)
add("email", email)
}
}
}
Printing the request body on my node server i get following:
[Object: null prototype] {
'{"name":"Test","email":"test#test.org"}': ''
}
When i send a request via Postman i get the following body:
[Object: null prototype] {
name: 'Test',
email: 'test#test.org'
}
And that's what i'm trying to produce with TornadoFX without success.
I am grateful for any help!

You are correctly setting the Content-Type header, but you're pushing a JsonModel as your data, and consequently the framework will convert it to a JSON structure by calling toJSON() on it. The application/x-www-form-urlencoded content type expects an URL encoded piece of data, so you need to convert your data into an url encoded string. I suggest adding a function to your domain model like this:
fun toURLEncoded() =
"name=${name.urlEncoded}&email=${email.urlEncoded}".byteInputStream(StandardCharsets.UTF_8)
This example uses an extension function called urlEncoded to avoid clutter. You can define that extension function like this for example:
fun Any.urlEncoded(): String = URLEncoder.encode(toString(), "UTF-8")
Now you just have to call api.post("api/register", userModel.toURLEncoded()) and you should be good to go.

Related

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

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.

Retrofit2 post request is successful but I'm not able to retrieve token from response body

So I'm trying to making a login(post) request to an API (https://reqres.in/api/login) with retrofit 2. The connection was successful as the response code is 200, when I did the same on Postman I received a response which contains a token string which I want but in android studio when I log the response body it gives different output. I am new to kotlin so I think I must be doing something wrong while retrieving response.
Output I'm receiving:
Response{protocol=h2, code=200, message=, url=https://reqres.in/api/login}
Output I want (token field)
{
"token": "QpwL5tke4Pnpja7X4"
}
Retrofit Builder
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(apiUrl)
.build()
val api : reqresAPI = retrofit.create(reqresAPI::class.java)
API Interface
interface reqresAPI {
// FOR REFERENCE
// https://reqres.in/api/login
// ANNOTATE WITH POST TO SEND DATA
#POST(value = "login")
fun sendData(
#Body user: User
): Call<ResponseModel> // CALL IS USED TO MAKE AN API CALL
}
Response Model
class ResponseModel{
val token : String = ""
get() = field
}
User model with 2 parameters email and password
class User (
val email :String,
val password :String
)
Calling API
val call = api.sendData(user)
call.enqueue(object : Callback<ResponseModel>{
override fun onResponse(call: Call<ResponseModel>, response: Response<ResponseModel>) {
Log.d("LOGGER", response.toString())
}
override fun onFailure(call: Call<ResponseModel>, t: Throwable) {
Log.d("LOGGER", "ERROR "+t.message.toString())
}
})
Please change this
class ResponseModel{
val token : String = ""
get() = field
}
to this
class ResponseModel{
#SerializedName("token")
val token : String
}

Relay Modern BadRequestError: Missing multipart field ‘operations’

I am trying to upload file to my server. using Altair i can do it without any error but when i use Relay.js for uploading it server throws following error.
BadRequestError: Missing multipart field ‘operations’ (https://github.com/jaydenseric/graphql-multipart-request-spec).
at Busboy.<anonymous> (/home/dotsinspace/Documents/dev/truck.pe__server/node_modules/.pnpm/graphql-upload#9.0.0_graphql#15.3.0/node_modules/graphql-upload/processRequest.js:362:11)
at Object.onceWrapper (events.js:420:28)
at Busboy.emit (events.js:326:22)
at Busboy.EventEmitter.emit (domain.js:486:12)
at Busboy.emit (/home/dotsinspace/Documents/dev/truck.pe__server/node_modules/.pnpm/busboy#0.3.1/node_modules/busboy/lib/main.js:37:33)
at /home/dotsinspace/Documents/dev/truck.pe__server/node_modules/.pnpm/busboy#0.3.1/node_modules/busboy/lib/types/multipart.js:52:13
at processTicksAndRejections (internal/process/task_queues.js:75:11)
Following are my graphql code and mutation which i am trying to commit.
#Graphql
graphql`
mutation AccountUploadMutation($profilePicture: Image!) {
AccountUpload(profilePicture: $profilePicture) {
message,
status
}
}
`
#Mutation
commitMutation(Environment, {
'mutation': AccountUploadMutation,
'variables': { 'profilePicture': v },
'uploadables': { 'file': v },
'onCompleted': (response, error) => Response({}, { response, error })
})
and I am totally confused about uploading part to..in uploadables you have to provide file..but my server looks for variable with profilePicture as image how can i deal with it.
It looks like you have an issue the multipart parsing configuration in your backend.
My guess is that the Relay Network is sending your GraphQL query in the mutlipart field "operation", but your backend is looking for the field "operations" (plural). To fix the error, confirm that your Network is sending the query in the operations field, or change your backend to read whatever field it's actually being sent on.
Another possibility is you're not sending your query in the multipart format at all. If you followed the Network documentation's example for sending your request, then you are just sending a JSON object, not a multipart form:
// Example from Relay docs. Sends a JSON object, not a multipart
// form as expected by your backend
function fetchQuery(
operation,
variables,
cacheConfig,
uploadables,
) {
return fetch('/graphql', {
method: 'POST',
headers: {
// Add authentication and other headers here
'content-type': 'application/json'
},
body: JSON.stringify({
query: operation.text, // GraphQL text from input
variables,
}),
}).then(response => {
return response.json();
});
}
// Create a network layer from the fetch function
const network = Network.create(fetchQuery);
If this is the case, write your fetchQuery function to fetch data using a multipart form. See this example: fetch post with multipart form data

Grails REST Client Plugin - PUT body content

Documentation seems to be lacking on both the plugin side as well as the HTTPBuilder side of things. I'm trying to submit some json through the put method, but it keeps telling me that put() doesn't like the map I am feeding it.
Does anyone have an example of a PUT using the Grails REST Client plugin? Here is what I've tried:
withHttp(uri: "http://foo/doo/roo") {
def bodyContent = [
pano: jsonText
]
def json = put(body: bodyContent)
if (json.stat == 'ok') {
wsr.success = true
}
}
Error:
No signature of method: com.wbr.pano.PanService.put() is applicable for argument types: (java.util.LinkedHashMap) values: [[body:
{
"class":"com.wbr.platform.Pano",
"errorMessage":"null",
"imageSize":0,
"id":26,
"completed":"2011-03-20 3:50:27.257",
"downloading":"2011-03-20 3:49:12.269",
"processing":"2011-03-20 3:49:42.911",
"uploading":"2011-03-20 3:50:12.107"
}
]]
HTTPBuilder doesn't have a put method. Try changing withHttp to withRest so that your statements are executed with the RESTClient. Also, I think by default the body is encoded as URL encoded, so you might need to specify requestContentType: groovyx.net.http.ContentType.JSON as another parameter to your put.
import static groovyx.net.http.ContentType.*
withRest(uri: "http://foo/doo/roo") {
def bodyContent = [
pano: jsonText
]
def json = put(body: bodyContent, requestContentType: JSON)
if (json.status == 200) {
wsr.success = true
}
}

Sending parameters in AJAX call

This has been asked before by others, but I have not been able to use their answers.
I am trying to sending some data by doing the following:
function addForumPost() {
var title = jQuery('#forumTitle').val();
var message = htmlEncode(jQuery('#message').htmlarea("toHtmlString"));
var tagNames = addTags();
var dataPost = $.toJSON({ title: 'testingsadf', message: message, tagNames: tagNames });
jQuery.ajax({
type: "POST",
url: "/Create",
data: dataPost,
dataType: "json",
success: function (result) {
}
});
}
I have checked, and doubled checked that the input contains data, but I only receive the data from the message parameter in my controller. The other two values are null. As you can see in the the example above, I have even assigned some static text to the title parameter, but I still only receive data for the message parameter.
The controller looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string title, string message, List<string> tagNames)
{
....
}
Your error is very simple. If you have an action which returns JsonResult then it means not that you have to send also JSON encoded input parameters to the method. Just remove the usage of $.toJSON() and use the object as a value of data parameter of jQuery.ajax (see the answer of Darin for example).
If you call ASMX Web Service instead of ASP.NET MVC action you have to use contentType: "application/json; charset=utf-8" and convert the values of all web-method parameters to JSON but in a little other way (see How do I build a JSON object to send to an AJAX WebService?). But ASP.NET MVC hat no such requirement. MVC converts the input parameters which you send with respect of Parse method (int.Parse, DateTime.Parse and so on) and not deserialize from JSON. So if you search for code examples look exactly which technology are used on the backend: ASP.NET MVC, ASMX web serveces or WFC.
Try setting the traditional parameter starting from jQuery 1.4. This works for me:
var title = 'title';
var message = 'message';
var tagNames = ['tag1', 'tag2'];
jQuery.ajax({
type: 'POST',
url: '/Home/Create',
data: { title: title, message: message, tagNames: tagNames },
dataType: 'json',
traditional: true,
success: function (result) {
}
});
With this action:
[HttpPost]
public ActionResult Create(
string title,
string message,
IEnumerable<string> tagNames
)
{
return Json(new
{
Title = title,
Message = message,
TagNames = tagNames
});
}
You don't have to post json for the type of action you described.
You don't have to manually assemble the fields into a map. Use .serializeArray()
var postData = $('.myform').serializeArray()