I am trying to do a PUT request to a Web API method using HttpClient. An almost identical POST request works as expected, but the PUT request results in a 400 (Bad Request) error. I'm not sure what I'm doing wrong.
Here is my web API controller code:
public HttpResponseMessage Post(object val)
{
Debug.WriteLine(val.ToString());
return new HttpResponseMessage(HttpStatusCode.OK);
}
public HttpResponseMessage Put(int id, object val)
{
Debug.WriteLine(id.ToString(), val.ToString());
return new HttpResponseMessage(HttpStatusCode.OK);
}
And then here is the client side code that attempts to call these methods. The POST request looks like this:
var client = new HttpClient();
var content = log.ToJsonContent();
var address = new Uri(_webApiBaseAddress + "logs");
client.PostAsync(address, content).ContinueWith(requestTask =>
{
requestTask.Result.EnsureSuccessStatusCode();
});
And the PUT request that doesn't work looks like this:
var client = new HttpClient();
var content = log.ToJsonContent();
var address = new Uri(_webApiBaseAddress + "logs/" + jobId);
client.PutAsync(address, content).ContinueWith(requestTask =>
{
requestTask.Result.EnsureSuccessStatusCode();
});
As you can see, I've stripped the functionality down as bare as I can get it, and I am still getting the error with the PUT request every time.
By the way, I can make the PUT request successfully using the RestSharp client. This code successfully calls the API method:
var client = new RestClient(_webApiBaseAddress);
var request = new RestRequest("logs/{id}", Method.PUT);
request.AddUrlSegment("id", jobId.ToString());
request.AddObject(log);
client.Execute(request);
The problem with doing it this way is that any object I pass in using AddObject just ends up being a string when it gets to the controller method. If I pass in an int, I want to get an int. HttpClient does this with the POST method, so I wanted to use that, but PUT throws an error. So I'm stuck with 2 half-working solutions, which don't add up to a full solution, unfortunately. I would appreciate feedback on either pattern to get this to work.
Edit:
Because it was mentioned below, here is my .ToJsonContent()
extension method:
public static StringContent ToJsonContent(this object obj)
{
var converter = new IsoDateTimeConverter();
converter.DateTimeStyles = DateTimeStyles.AdjustToUniversal;
var json = JsonConvert.SerializeObject(obj, converter);
var content = new StringContent(json, Encoding.UTF8, "application/json");
return content;
}
Is ToJsonContent
functionality that you wrote? If so, it might be worth seeing it to make sure that you are supplying the correct Content-Type and generating a proper JSON format. To eliminate the JSON being the problem you could do a test like this:
var client = new HttpClient();
var content = new StringContent(
@"{""message"": ""this is a log message"" }",
Encoding.UTF8,
"application/json");
var address = new Uri(_webApiBaseAddress + "logs/" + jobId);
client.PutAsync(address, content).ContinueWith(task =>
{
task.Result.EnsureSuccessStatusCode();
});
Noice I supply application/json
as a parameter. I know web API does crazy stuff if it doesn't know what media type your request is in. The fact that your RestClient test does work has me thinking the JSON you are sending is not valid.
The second part that might help you is that if you are testing this out in something like a console app that could potentially close before the async call really gets initialized and sent you won't get your response. You could test this by just putting a .Wait()
after your ContinueWith
but i suspect that if you're actually getting a 400 response this wouldn't be the case.