I am building a small Web API for syncing data and pulling down the objects works great, but pushing my objects up doesn't work no matter what I have tried.
Edited to reflect some changes:
Here is my Controller:
[System.Web.Mvc.HttpPost]
public void UpdateTasks([FromBody] string s)
{
Console.WriteLine(s);
}
Here is my Client code:
HttpContent c = new StringContent("1234");
HttpClient client = new HttpClient();
c.Headers.ContentType = new MediaTypeHeaderValue("application/json");
client.BaseAddress = new Uri("http://localhost/QAQC_SyncWebService/Tasks/UpdateTasks/");
var resp = client.PostAsync(client.BaseAddress, c).Result;
I can get a value though if I put it in the URI, but the string content alone doesn't seem to work.
Try
[HttpPut]
public void UpdateTasks([FromBody]string s)
{
Console.WriteLine(s);
}
Please also note:
[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.
from http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/
The line you are initializing client.BaseAddress looks a bit off.
HttpContent c = new StringContent("1234");
HttpClient client = new HttpClient();
c.Headers.ContentType = new MediaTypeHeaderValue("application/json");
client.BaseAddress = new Uri("http://localhost/QAQC_SyncWebService/Tasks/UpdateTasks");
var resp = client.PutAsync(client.BaseAddress, c).Result;
The PutAsync method is expecting the full URI, not just a method. Read more here: https://msdn.microsoft.com/en-us/library/hh138168(v=vs.118).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
Related
I'm fairly new to .NET's HTTPClient class, hence kindly excuse if I sounded noob. I'm tryin to replicate Postman's POST request in C# .Net and written following code. However I'm not getting any response but StatusCode: 404. Could someone assist understanding where I'm going wrong?
Also I'd like to understand, how do set Body in following code.
var httpClient = new HttpClient
{
BaseAddress = new Uri("https://testURL.com"),
Timeout = TimeSpan.FromMinutes(10)
};
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("audio/wav"));
httpClient.DefaultRequestHeaders.Add("Authorization", "Basic ldjfdljfdlfjdsjfdsl");
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("model", "Test"),
});
var result = httpClient.PostAsync("api/v1/recognize", content).Result;
Here is what I'm doing in Postman and it works:
"Params" in Postman refers to query parameters which are appended to the URL. You'll see that the URL in Postman contains the parameters you added in the "Params" tab:
However, it seems those are just dummy values you've entered so perhaps you don't need them? In any case, the way you add query parameters to the request for HttpClient is a little different as it needs to be added to the URL.
After that you also need to add the audio file as content to your request. At the moment you're setting the "Accept" header to "audio/wav" but you probably want to set the "Content-Type" header instead (or are you expecting a WAV file to be returned in the response too?).
As far as I can see this is what you're missing:
using (var httpClient = new HttpClient())
{
httpClient.Timeout = TimeSpan.FromMinutes(10);
// Set request headers
httpClient.DefaultRequestHeaders.Add("Authorization", "Basic ldjfdljfdlfjdsjfdsl");
// Set query parameters
var uriBuilder = new UriBuilder("https://testURL.com/api/v1/recognize");
uriBuilder.Query = "model=Test";
// Build request body
// Read bytes from the file being uploaded
var fileBytes = File.ReadAllBytes(wavFilePath);
// Create request content with metadata/headers to tell the
// backend which type of data (media type) is being uploaded
var byteArrayContent = new ByteArrayContent(fileBytes);
byteArrayContent.Headers.ContentType = MediaTypeHeaderValue.Parse("audio/wav");
// Wrap/encode the content as "multipart/form-data"
// See example of how the output/request looks here:
// https://dotnetfiddle.net/qDMwFh
var requestContent = new MultipartFormDataContent
{
{byteArrayContent, "audio", "filename.wav"}
};
var response = await httpClient.PostAsync(uriBuilder.Uri, requestContent);
}
I haven't tested this of course against your application, but it should be something along the lines of this. It might be that the backend doesn't expect "multipart/form-data" and just needs the "audio/wav". I can't see the output headers in your Postman screenshots, but if so, you can use byteArrayContent directly instead of wrapping it in MultipartFormDataContent.
Note: Don't use httpClient.PostAsync(...).Result. If you want to use the asynchronous method, you should await it. Depending on your code, using Result might give you problems if you're not careful. And remember to dispose the HttpClient after use (easiest solution is to use a using statement). If you plan on reusing the HttpClient for more requests, you can avoid disposing it until you're done.
Someone's probably done this before but I can't seem to formulate the question properly to find results. I want to make AJAX calls from a view, but I can't directly call the external API from javascript because there's a key that I can't expose. My idea is to have another controller action that I call from the page that calls the actual external REST API I want to get data from and just passes it on as a JSON. I see lots of examples of getting a JSON through C# and deserializing it but not many where you get a JSON and then return it and consume it from the view. Any help appreciated.
public JsonResult GetStuff()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(URL);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("Stuff/?Id=" + id).Result;
*code to take response and pass it on as a JSON that I can consume from Javascript
}
Here is what I recommend.
[HttpGet("myapi/{id}");
public async Task MyApi(int id) {
// Replace these lines as needed to make your API call properly.
using HttpClient client = new() {
BaseAddress = REMOTE_SERVER_BASE
}
// Make sure to properly encode url parameters if needed
using HttpResponseMessage response = await client.GetAsync($"myapi/{id}");
this.HttpContext.Response.StatusCode = (int)response.StatusCode;
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers) {
this.HttpContext.Response.Headers[header.Key] = new StringValues(header.Value.ToArray());
}
await response.Content.CopyToAsync(this.HttpContext.Response.Body);
}
This will copy all the common response fields such as status code, headers, and body content, over to your response.
This code isn't tested so you might have to tweak it a bit but it should be enough to get you started.
I am trying to get one API (reports API) to talk to another API (stored procedure runner API) which is a solution we are sort of forced to adopt given that we created the first API in .NET Core 2.2 and the Sap.Data.SQLAnywhere.v4.5 drivers only play nice with the .NET 4.7.2 framework. So we segregated them to compensate. All the answers I have seen over the last 4 or 5 hours leads me to believe I am doing this correctly but it still doesn't work.
I can hit the stored procedure runner from Postman with a json/text body just fine and get results from the database. However, when I try to hit this from C# I was first getting Unsupported Media Type which I think I fixed but now I get 500 errors and when debugging through to the stored procedure runner from the reportsAPI I notice that I don't get a parameter passed from the body.
[HttpPost]
[Route("api/Reports/GetStuff")]
[ResponseType(typeof(ResponseObject))]
public ResponseObject GetStuff([FromBody]string report)
{
var response = new ResponseObject();
try
{
response = new ReportService().RunStuff(report);
}
catch (Exception e)
{
throw;
}
return response;
}
The above is the SprocRunnerAPI and I get as far as
response = new ReportService().RunStuff(report);
before it fails out because it has nothing in "report".
public class ApiService
{
public static HttpClient ApiClient { get; set; }
public static void InitializeClient()
{
ApiClient = new HttpClient();
//ApiClient.BaseAddress = new Uri("");
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
This is where I initialize everything then use it in the following class method:
public ResponseObject RunReportToCSV(string report)
{
var rep = new ResponseObject();
ApiClient.ApiService.InitializeClient();
string url = #"http://localhost/EnterpriseReportRunner/api/reports/GetStuff";
var httpContent = new StringContent(report, Encoding.UTF8, "application/json");
//ServicePointManager.Expect100Continue = false; I honestly don't know where this goes... or if it is needed.
var response = ApiClient.ApiService.ApiClient.PostAsync(url , httpContent).Result;
if (response.IsSuccessStatusCode)
{
rep = response.Content.ReadAsAsync<ResponseObject>().Result;
}
else
{
throw new Exception(response.ReasonPhrase);
}
return rep;
}
I get as far as
var response = ApiClient.ApiService.ApiClient.PostAsync(url, httpContent).Result;
when it calls the aforementioned api method externally and it fails out. I had noticed no differences between the bodies in what I put in C# and what I put in Postman nor did I notice a difference looking through Fiddler on the Headers when making either call. I am seriously confused as to why what appears to be what I have seen everywhere used, that this is not working.
Thank you.
Adding Stack Trace from the SprocRunnerAPI… this isn't much help because I know exactly why this happens. By this point I expect a JsonSerialized object and I don't have it because it wasn't passed from the first API to the second.
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at EnterpriseReportRunner.Service.ReportService.RunStuff(String report) in C:\Workspace\Solution.NET\LOB\InternalReportsAdminConsole Solution\EnterpriseReportRunner\EnterpriseReportRunner\Service\ReportService.cs:line 27
at EnterpriseReportRunner.Controllers.ReportsController.GetStuff(String report) in C:\Workspace\Solution.NET\LOB\InternalReportsAdminConsole Solution\EnterpriseReportRunner\EnterpriseReportRunner\Controllers\ReportsController.cs:line 67
To be clear this is the stack from the SprocRunnerAPI not the ReportsAPI that calls it. I am trying to find the body inside the HttpContext to see the difference between the Postman call and the C# call but can't seem to find it, is it really buried in the InputStream?
The issue seems to be one that I thought would be redundant. The bottom line is that even if you expect a serialized JSON object you need to serialize it again, otherwise when you pass the StringContent via the HttpClient the body contents don't show in the receiving controller's parameter for consumption.
var httpContent = new StringContent(JsonConvert.SerializeObject(report), Encoding.UTF8, "application/json");
Even though "report" is already a string... really weird to me. But that solves it.
Found out that the strings that come into a controller are also wrapped inside another object so .Root seems to be your friend when deserializing. At least I learned something from the experience.
Thank you for all your input. Might want to link this to this other question.
I have been attempting to do a simple POST request to a basic Flask API, and no matter what I try it simply will do http://localhost:5000/ rather than the http://localhost/?test_key=test_value that I want it to do. C# is not my strongest language, but I have tried a bunch of different methods that I found online. I don't think it has to do with my method being bad at this point, I more so think that I'm missing a major piece entirely.
Here is the most recent attempt I tried (keep in mind this is a snippet of a far larger project, but it doesn't involve any other pieces of it):
class DisputeResponse
{
public int id;
public string res;
public string test_key;
}
[HttpPost]
public async Task<JsonResult> TestResponse(int id, string res)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:5000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
var dispRes = new DisputeResponse();
dispRes.test_key = "test_value";
var result = await client.PostAsync("http://localhost:5000/",
new StringContent(JsonConvert.SerializeObject(dispRes), Encoding.UTF8, "application/json"
));
result.EnsureSuccessStatusCode();
}
catch (Exception e)
{
System.Diagnostics.Trace.TraceInformation(e.Message);
}
return null;
}
Here is the output when I use Postman (works perfectly):
127.0.0.1 - - [15/Apr/2019 16:26:28] "POST /?test_key=test_value HTTP/1.1" 200 -
And here is when I try to use C# code:
127.0.0.1 - - [15/Apr/2019 16:36:54] "POST / HTTP/1.1" 500 -
EDIT: Sorry I had some extraneous lines that I removed.
The HTTPClient is putting the parameters inside the body of the request. If you would like to add it to the URI you can do so by modifying the URI string. If you are not posting anything to the body, I would modify the FLASK API to receive a GET instead of a POST.
Typically I would only use query string parameters in the URI string for HTTPGet requests, even though there is no rule stopping you from doing this with a HTTPPost. Additionally, I would only include my parameters for an HTTPPost request within the body. Again, no rule for this but just what I do.
Here are a few examples to help you along with the HTTPClient post async method:
https://csharp.hotexamples.com/examples/-/HttpClient/PostAsync/php-httpclient-postasync-method-examples.html
I'm new to this one so please be gentle.
I am adding a method to a webservice so that it can recieve POSTs from another.
The POST contains a JSON object in the body with parameters I need in the receiving solution.
I am using HttpClient's PostAsync method to send the data (as FormUrlEncodedContent) and I'm trying to recieve it as HttpRequestMessage.
When i try to compile and build I get this error in my recieving service
To be XML serializable, types which inherit from IEnumerable must have
an implementation of Add(System.Object) at all levels of their
inheritance hierarchy. System.Net.Http.Headers.HttpContentHeaders does
not implement Add(System.Object).
The odd thing is that I use these both of these in the POSTing webservice (I use ajax on a page to test it) and don't get this problem.
This is the sender:
string url = *url*;
HttpClient client = new HttpClient();
Dictionary<string, string> parameters;
parameters = new Dictionary<string, string>
{
{ "*variablename1", *string* },
{ "variablename2", *string* },
{ "variablename3", *string* },
{ "variablename4", *string* }
};
var content = new FormUrlEncodedContent(parameters);
HttpResponseMessage response = client.PostAsync(url, content).Result;
And this is what is supposed to be receiving it...
public void Receive(HttpRequestMessage request)
{
string data = request.Content.ReadAsStringAsync().Result;
*Database object* = new *Database object*();
string decodedString = HttpUtility.UrlDecode(data);
*Stuff that processes decodedString into a dictionary and puts it in database*
What I'd like is to know how I can get this to work, so an alternative to HttpRequestMessage that doesn't depend on HttpContentheaders having an add method, or another way of sending the post that doesn't require them on the recieving end.
Apologies for vague bits of code, I've only been a programmer for three months and my company works with a lot of confidential data, I'm just covering my back.