How do I send a HTTP PUT request with HttpClient? - c#

I have a web service that I'm trying to write to, but every time I try to send a PUT request to it I get a 'Bad Request(400)' Error. I'm using the ASP.NET MVC framework and I have everything set up in one of my controllers. I have a model as well that models the data that should be sent to the API. This is how I have everything set up:
PUT FUNCTION
public string putFunction(ItemOject item)
{
// PROVIDED DETAILS
string API_PATH = "API_PATH";
// CONTENT
var content = new StringContent(JsonConvert.SerializeObject(item));
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Key1", KEY_1);
client.DefaultRequestHeaders.Add("Key2",KEY_2);
client.DefaultRequestHeaders.Add("Key3", KEY_3);
// KEYS and BASE_URL are defined outside the function
var response = client.PutAsync($"{BASE_URL}{API_PATH}", content).Result;
return response.ToString();
}
This is what the param 'item' is getting passed:
ItemObject item = new ItemObject
{
Name = "Test2",
Errors = null,
ID = "8c785cd5-673a-4b3c-b97d-b8446dad401a",
OwnerID = "30e5772a-a11c-4172-96ae-cc850bd6509a"
};
And this is the response I get:
StatusCode: 400, ReasonPhrase: 'Bad Request'
I'm positive all the KEYS are setup correctly and the URL is correct. The endpoint does work because I've tested it in Postman with no issue. The only thing now is that I can't send the HTTP PUT request from this function. I've played around to make sure that the data I'm sending in Postman is almost exactly what I'm sending from this function. I don't know what I'm missing or what more tests I could be trying.
This is my Postman request:
{"Item":
{
"Errors": null,
"ID": "8c785cd5-673a-4b3c-b97d-b8446dad401a",
"Name": "Test1",
"OwnerID": "00000000-0000-0000-0000-000000000000"
}
}
And that works fine in Postman if I were to again send a GET request, it would retieve that said data. But I can't do that within ASP.NET without getting the "Bad Request(400)" Error.
What I've tried so far:
Can't find how to use HttpContent
Send ASP.NET MVC HttpContext to Web Api HttpContext
https://www.c-sharpcorner.com/article/asp-net-web-api-using-mvc-entity-framework-and-httpclient-for-get-and-post-with/

Serializing your current object would result in this:
{
"Errors": null,
"ID": "8c785cd5-673a-4b3c-b97d-b8446dad401a",
"Name": "Test1",
"OwnerID": "00000000-0000-0000-0000-000000000000"
}
But what you are passing into Postman is this:
{
"Item":
{
"Errors": null,
"ID": "8c785cd5-673a-4b3c-b97d-b8446dad401a",
"Name": "Test1",
"OwnerID": "00000000-0000-0000-0000-000000000000"
}
}
So your model should look more like:
ItemObject item = new ItemObject()
{
Item = new ItemSubObject()
{
Name = "Test2",
Errors = null,
ID = "8c785cd5-673a-4b3c-b97d-b8446dad401a",
OwnerID = "30e5772a-a11c-4172-96ae-cc850bd6509a"
}
};

Related

Sending a json object in a post method

I am trying to create a virtual machine through a post method but I have not been able to create it since I cannot send the json file correctly.
This is my code:
// Create a json object
var deploy = new Deploy
{
name = "RandomName",
folder = "folder_3044",
resource_pool = "resgroup-4035"
};
//Change it into a json file
var deployResult = JsonConvert.SerializeObject(deploy);
//Change this file into a string so we can send it to the post method
var jsonContent = new StringContent(deployResult);
//Make post and send the needed information
var postResponse = await httpClient.PostAsync("URI", jsonContent);
return (content);
When executing my code in Postman it gives me a false positive because it sends something but it seems that it does not send the json as I wanted but the code below:
{
"headers": [
{
"key": "Content-Type",
"value": [
"text/plain; charset=utf-8"
]
},
{
"key": "Content-Length",
"value": [
"97"
]
}
]
}
Can anyone help me with this? I just need to send that json in my post like I do when sending from Postman.
Ps: I use .Net core and I can't change even if I want to.
This is the json that should be sent for it to work:
{
"name": "RandomName",
"placement": {
"folder": "folder_3044",
"resource_pool": "resgroup-4035"
}
}
Thank you to #Peter Csala's comment: When I specified the content-type as you advised, it worked. Just for future references, we need these two lines of code. (Don't forget to include the assembly System.Text on top.)
using System.Text;
new StringContent(deployResult, Encoding.UTF8, "application/json");

View pre-deserialized response from GraphQL query

I have a simple GraphQL query that I'm making out to a server, and trying to use GraphQL.Client.Serializer.Newtonsoft to deserialize it. I'm getting a response, but the response does not make the NewtonsoftJsonSerializer happy. It complains:
This converter can only parse when the root element is a JSON Object.
Okay, fair enough. The response must not be what was expected. But how can I view the response to see what may be wrong? The GraphQLHttpClient.SendQueryAsync<>() method requires a type to try to deserialize to...which is the part that is failing. I don't see a way to just get the text response so I can see what is wrong with it.
I've included sample code at the bottom, but the only real line of interest (I believe) is the last one:
var graphQLResponse = await graphQLClient.SendQueryAsync<object>(theRequest);
Is there some way to just get the text of the response?
var graphQLClient = new GraphQLHttpClient("https://<TheDomain>.com/graphql/api/v1", new NewtonsoftJsonSerializer());
graphQLClient.HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer <MyKey>");
graphQLClient.HttpClient.DefaultRequestHeaders.Add("Accept", "application/json");
var theRequest = new GraphQLRequest
{
Query = "{ __schema { queryType { fields { name } } } }"
};
var graphQLResponse = await graphQLClient.SendQueryAsync<object>(theRequest);
Using Fiddler Everywhere, I was able to pull in the request and response, both of which seem superficially valid (I updated the query in the question to match the current query I'm passing in...a general schema query)
Request:
{
"query": "{ __schema { queryType { fields { name } } } }"
}
Response:
(I've redacted the names of the endpoints and removed much of the repetition from the middle of the response. But the rest is unchanged, and looks superficially okay to me...in particular, it looks like valid JSON, so I'm unclear why the converter is complaining.)
{
"errors": [],
"data": {
"__schema": {
"queryType": {
"fields": [
{
"name": "getData1"
},
{
"name": "getData2"
},
...
<a bunch more here>
...
{
"name": "getData100"
}
]
}
}
},
"extensions": null,
"dataPresent": true
}
After a few hit and trials and discussion with the O.P, it was found that while initializing GraphQLHttpClient with GraphQL.Client.Serializer.Newtonsoft, the serializer is not correctly doing the deserialization of the response despite the response being a valid JSON string.
After switching to GraphQL.Client.Serializer.SystemTextJson, the response is being correctly parsed as expected which suggests that there could be a bug in the GraphQL.Client.Serializer.Newtonsoft serializer

Why is Asp.net Core returning JSON instead of a CSV file? [duplicate]

I am currently working with ASP.NET Core RC2 and I am running into some strange results.
So I have an MVC controller with the following function:
public HttpResponseMessage Tunnel() {
var message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StringContent("blablabla", Encoding.UTF8);
message.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain");
message.Headers.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue {
NoCache = true
};
return message;
}
If I call this with postman with an Accept header set to text plain I get this response:
{
"Version": {
"Major": 1,
"Minor": 1,
"Build": -1,
"Revision": -1,
"MajorRevision": -1,
"MinorRevision": -1
},
"Content": {
"Headers": [
{
"Key": "Content-Type",
"Value": [
"text/plain"
]
}
]
},
"StatusCode": 200,
"ReasonPhrase": "OK",
"Headers": [
{
"Key": "Cache-Control",
"Value": [
"no-cache"
]
}
],
"RequestMessage": null,
"IsSuccessStatusCode": true
}
I really do not understand how this is the generated reponse to the above controller. It is basically a JSON serialization of the entire message itself and does in no way contain the "blablabla" I intended to send.
The only way I have gotten the desired result is by making my controller function return string instead of HttpResponse, but that way I am unable to set headers like CacheControl
So my question is: why do I get this strange response? It seems like very weird behaviour to me
According to this article, ASP.NET Core MVC does not support HttpResponseMessage-returning methods by default.
If you want to keep using it, you can, by using WebApiCompatShim:
Add reference to Microsoft.AspNetCore.Mvc.WebApiCompatShim to your project.
Configure it in ConfigureServices(): services.AddMvc().AddWebApiConventions();
Set up route for it in Configure():
app.UseMvc(routes =>
{
routes.MapWebApiRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
If you want to set Cache-Control header with string content, try this:
[Produces("text/plain")]
public string Tunnel()
{
Response.Headers.Add("Cache-Control", "no-cache");
return "blablabla";
}
In ASP.NET Core, modify the response as it travels through the pipeline. So for headers, set them directly as in this answer. (I've tested this for setting cookies.) You can also set the HTTP status code this way.
To set content, and therefore use a specific formatter, follow the documentation Format response data in ASP.NET Core Web API. This enables you to use helpers such as JsonResult() and ContentResult().
A complete example translating your code might be:
[HttpGet("tunnel")]
public ContentResult Tunnel() {
var response = HttpContext.Response;
response.StatusCode = (int) HttpStatusCode.OK;
response.Headers[HeaderNames.CacheControl] = CacheControlHeaderValue.NoCacheString;
return ContentResult("blablabla", "text/plain", Encoding.UTF8);
}

Trying to PUT string with Postman in local C# web api, always null or fails

I currently have this WEB API running locally:
// POST api/CsvParse
[HttpPut]
public void Put([FromBody]string value)
{
if (string.IsNullOrEmpty(value))
throw new Exception("Input is null or empty.");
}
I currently have it running locally, and am sending a string to the put using POSTMAN. I have selected the body tab, and have the string pasted into the raw body tab:
It states that my text is unsupported, or when I add a break point the value is null or I get the error describing the format is incorrect.
What am I doing wrong?
That's because there is no media type formatter that can serialize a raw string into your model (your route parameter with [FromBody] attribute).
A quick and dirty workaround is to directly read the body of your request as a string:
[HttpPut]
public async Task<HttpResponseMessage> Put(HttpRequestMessage request)
{
var myCsv = await request.Content.ReadAsStringAsync();
// do stuff with your string
return new HttpResponseMessage(HttpStatusCode.OK);
}
As an alternative you could implement a custom media type formatter yourself, as per this answer.
Change the media type to x-www-form-urlencoded rather than multipart/form-data.
Also, WebAPI is particular about FromBody parameters.
http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/
For you, I think this is the relevant part:
[FromBody] parameters must be encoded as =value
The final hurdle remaining is that Web API requires you to pass
[FromBody] parameters in a particular format. That’s the reason why
our value parameter was null in the previous example even after we
decorated the method’s parameter with [FromBody].
Instead of the fairly standard key=value encoding that most client-
and server-side frameworks expect, Web API’s model binder expects to
find the [FromBody] values in the POST body without a key name at all.
In other words, instead of key=value, it’s looking for =value.
This part is, by far, the most confusing part of sending primitive
types into a Web API POST method. Not too bad once you understand it,
but terribly unintuitive and not discoverable.
Try adding a content type of text/plain
There is a similar Q&A here
I found that solution #1 worked for me as I was trying to PUT JSON containing the Key/Value pair. So originally my JSON looked like this
{
"subscriber": {
"Id": "2",
"subscriptions":[
{
"Name": "Subscription 1",
"Id": "18",
"IsSubscribed": false
},
{
"Name": "Subscription 2",
"Id": "19",
"IsSubscribed": false
},
{
"Name": "Subscription 3",
"Id": "20",
"IsSubscribed": false
}
]
}
}
But I modified it to become
{
"Id": "2",
"subscriptions":[
{
"Name": "Subscription 1",
"Id": "18",
"IsSubscribed": false
},
{
"Name": "Subscription 2",
"Id": "19",
"IsSubscribed": false
},
{
"Name": "Subscription 3",
"Id": "20",
"IsSubscribed": false
}
]
}
And that worked. My PUT request from Postman was recognised in my C# web api using [FromBody]
Just to add my bit, there is one more solution to pass primitives into a POST or a PUT method. Just specify the model as JObject. ASP.Net core web api then binds incoming JSON object(containing primitives like string) into JObject model object.
Your code would look like this:
// POST api/CsvParse
[HttpPut]
public void Put([FromBody]JObject value)
{
//access your string data
string data = value[SPECIFY_KEY_HERE];
if (string.IsNullOrEmpty(data))
throw new Exception("Input is null or empty.");
}

Web API action executes successfully but I see no response in the client application

From an ASP.NET MVC app, I make a POST request to an action on my Web API. The action in the API executes successfully. When I make the same request in a REST client such as Postman for Chrome, I see a valid Json response.
However, if I make the call from my MVC app, I don't see any response back and the flow of control disappears after making the POST request to the API. The MVC app keeps waiting, as though.
Here's my code.
Web API
[HttpPost]
[ActionName("CreateNewUserFromAccountInfo")]
public IUser CreateNew(NewUserAccountInfo newUserAccountInfo)
{
return _provider.CreateNew(newUserAccountInfo.FullName, newUserAccountInfo.Email, newUserAccountInfo.PasswordHash);
}
MVC app controller
IUser user = new WebAPIClient()
.PostAsJsonAsync<NewUserAccountInfo, IUser>
("api/membership/CreateNewUserFromAccountInfo",
newUserAccountInfo
).Result;
From the WebAPIClient class from within a class library referenced from the MVC app:
public async Task<R> PostAsJsonAsync<T, R>(string uri, T value)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
await PrintRequestBodyToDebugWindowAsync(value, new JsonMediaTypeFormatter());
// The flow of control just disappears after this line
// and never gets to the next line
var response = await client.PostAsJsonAsync(uri, value);
// The flow of control never gets here
if (response.IsSuccessStatusCode) return await response.Content.ReadAsAsync<R>();
else return default(R);
}
}
RESULTS RETURNED WHEN REQUEST MADE VIA POSTMAN (a REST client)
Request Uri: http://localhost:54488/api/membership/CreateNewUserFromAccountInfo
Method: POST
Request Body: {"FullName":"Aamir Khan","Email":"aamir#khan.com","PasswordHash":"hello"}
Response:
{
"Id": 15,
"IsExternal": false,
"ExternalId": null,
"ExternalProviderName": null,
"UserType": "Healthcare Seeker",
"Verified": false,
"VerificationCode": "375b8d2f-67df-433d-9e95-c84c30462b80",
"VerificationCodeExpiresOn": "9999-12-31T23:59:59.9999999",
"PasswordResetCode": null,
"DefaultPasswordChanged": false,
"HasFilledBasicProfile": false,
"CreationDateTime": "2014-07-01T20:47:45.1405416+05:30",
"FullName": {
"Id": 2,
"Name": "FullName",
"Value": "Aamir Khan",
"Description": null,
"PrivacyLevel": {
"Id": 1,
"Name": "Private",
"Description": "Visible only to me"
}
},
"Email": {
"Id": 1,
"Name": "Email",
"Value": "aamir#khan.com",
"Description": null,
"PrivacyLevel": {
"Id": 1,
"Name": "Private",
"Description": "Visible only to me"
}
},
"Profiles": [
null,
null,
null
],
"BasicProfile": null,
"MedicalProfile": null,
"EmergencyProfile": null
}

Categories