ObjectContent not working but StringContent does












0














I'm trying to write an overload for HttpClient.PutAsJsonAsync<T> which allows headers to be set on each request. Looking at the source code for that method, it creates an ObjectContent<T> internally, which I tried to emulate below.



However, my target server always receives a null body when binding (using [FromBody] Form form).



If I change the request to be a StringContent and manually serialize the body, the request succeeds! What could be the difference between the two implementations?



Even more strangely, if I call await content.ReadAsStringAsync() before SendAsync then it also works!



My thoughts are it could be either:




  • The JSON is being serialized differently (But I'm not configuring any Json.NET globals anywhere.) The obvious problem would be different capitalization, but as far as I know, JSON.net is case insensitive when parsing, and my server will be using JSON.net

  • I'm not awaiting something and the content is being sent before it serialises fully. I can't spot any un-awaited Tasks in my code though, and the entrypoint is a standard async Main()


My environments is:




  • Client is a simple netcoreapp2.1 exe, running in a microsoft/dotnet:2.1-sdk-alpine docker image.

  • Server is a .NET framework MVC5 WebApi, with many similar APIs which other clients can call fine


The code below is a trimmed down version of what my client is calling.



public static class UserUpdater
{
public static async Task UpdateAsync(List<string> users)
{
using (var client = new HttpClient())
{
var form = new { Users = users };
var headers = new Dictionary<string, string>
{
["Authorization"] = "Bearer " // Real JWT here
};

var response = await client.PutAsJsonAsync(new Uri("http://localhost/api/app/v1/gateway-users"), form, headers);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException("Response status code does not indicate success: " +
response.StatusCode + "nn" +
await response.Content.ReadAsStringAsync());
}
}
}

public static async Task<HttpResponseMessage> PutAsJsonAsync<T>(this HttpClient client, Uri requestUri, T value,
IEnumerable<KeyValuePair<string, string>> headers, CancellationToken cancellationToken = default)
{
var content = new ObjectContent<T>(value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null);
////var content = new StringContent(JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json"); // This works fine
var message = MakeRequestMessage(HttpMethod.Put, requestUri, headers, content);
return await client.SendAsync(message, cancellationToken);
}

private static HttpRequestMessage MakeRequestMessage(
[NotNull] HttpMethod method,
[NotNull] Uri requestUri,
[CanBeNull] IEnumerable<KeyValuePair<string, string>> headers,
[CanBeNull] HttpContent content = null)
{
var message = new HttpRequestMessage(method, requestUri);

if (headers != null)
{
foreach (var header in headers)
{
message.Headers.Add(header.Key, header.Value);
}
}

if (content != null)
{
message.Content = content;
}

return message;
}


If it helps, this is the server side (MVC5 WebApi):



    [Route("")]
[ValidateModel]
public IHttpActionResult Put([Required] [FromBody] GatewayUserCollectionForm form)
{
Ensure.NotNull(form, nameof(form)); // This throws since form is null

this.manager.UpdateGatewayUsers(form.Users.ToList());
return this.Ok();
}

public class GatewayUserCollectionForm
{
[Required]
public List<string> Users { get; set; }
}









share|improve this question
























  • Did you make sure that your api is returning 'application/json'?
    – wannadream
    Nov 12 '18 at 22:48










  • @wannadream It would normally, but it throws a NullReferenceException since it reads the bound form (which is null). It therefore returns a HTTP 500 with no body
    – berkeleybross
    Nov 12 '18 at 22:50












  • Can you change this line to this? var response = await client.PutAsJsonAsync(new Uri("localhost/api/app/v1/gateway-users"), users, headers); I do not see it is necessary to wrap it with an anonymous object.
    – wannadream
    Nov 12 '18 at 22:56












  • @wannadream unfortunately not, the server is expecting an object which contains a list. I'll update with the server code/models but I dont think its the right direction :/
    – berkeleybross
    Nov 12 '18 at 23:02










  • I see. The structure is right. Just double confirm.
    – wannadream
    Nov 12 '18 at 23:11


















0














I'm trying to write an overload for HttpClient.PutAsJsonAsync<T> which allows headers to be set on each request. Looking at the source code for that method, it creates an ObjectContent<T> internally, which I tried to emulate below.



However, my target server always receives a null body when binding (using [FromBody] Form form).



If I change the request to be a StringContent and manually serialize the body, the request succeeds! What could be the difference between the two implementations?



Even more strangely, if I call await content.ReadAsStringAsync() before SendAsync then it also works!



My thoughts are it could be either:




  • The JSON is being serialized differently (But I'm not configuring any Json.NET globals anywhere.) The obvious problem would be different capitalization, but as far as I know, JSON.net is case insensitive when parsing, and my server will be using JSON.net

  • I'm not awaiting something and the content is being sent before it serialises fully. I can't spot any un-awaited Tasks in my code though, and the entrypoint is a standard async Main()


My environments is:




  • Client is a simple netcoreapp2.1 exe, running in a microsoft/dotnet:2.1-sdk-alpine docker image.

  • Server is a .NET framework MVC5 WebApi, with many similar APIs which other clients can call fine


The code below is a trimmed down version of what my client is calling.



public static class UserUpdater
{
public static async Task UpdateAsync(List<string> users)
{
using (var client = new HttpClient())
{
var form = new { Users = users };
var headers = new Dictionary<string, string>
{
["Authorization"] = "Bearer " // Real JWT here
};

var response = await client.PutAsJsonAsync(new Uri("http://localhost/api/app/v1/gateway-users"), form, headers);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException("Response status code does not indicate success: " +
response.StatusCode + "nn" +
await response.Content.ReadAsStringAsync());
}
}
}

public static async Task<HttpResponseMessage> PutAsJsonAsync<T>(this HttpClient client, Uri requestUri, T value,
IEnumerable<KeyValuePair<string, string>> headers, CancellationToken cancellationToken = default)
{
var content = new ObjectContent<T>(value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null);
////var content = new StringContent(JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json"); // This works fine
var message = MakeRequestMessage(HttpMethod.Put, requestUri, headers, content);
return await client.SendAsync(message, cancellationToken);
}

private static HttpRequestMessage MakeRequestMessage(
[NotNull] HttpMethod method,
[NotNull] Uri requestUri,
[CanBeNull] IEnumerable<KeyValuePair<string, string>> headers,
[CanBeNull] HttpContent content = null)
{
var message = new HttpRequestMessage(method, requestUri);

if (headers != null)
{
foreach (var header in headers)
{
message.Headers.Add(header.Key, header.Value);
}
}

if (content != null)
{
message.Content = content;
}

return message;
}


If it helps, this is the server side (MVC5 WebApi):



    [Route("")]
[ValidateModel]
public IHttpActionResult Put([Required] [FromBody] GatewayUserCollectionForm form)
{
Ensure.NotNull(form, nameof(form)); // This throws since form is null

this.manager.UpdateGatewayUsers(form.Users.ToList());
return this.Ok();
}

public class GatewayUserCollectionForm
{
[Required]
public List<string> Users { get; set; }
}









share|improve this question
























  • Did you make sure that your api is returning 'application/json'?
    – wannadream
    Nov 12 '18 at 22:48










  • @wannadream It would normally, but it throws a NullReferenceException since it reads the bound form (which is null). It therefore returns a HTTP 500 with no body
    – berkeleybross
    Nov 12 '18 at 22:50












  • Can you change this line to this? var response = await client.PutAsJsonAsync(new Uri("localhost/api/app/v1/gateway-users"), users, headers); I do not see it is necessary to wrap it with an anonymous object.
    – wannadream
    Nov 12 '18 at 22:56












  • @wannadream unfortunately not, the server is expecting an object which contains a list. I'll update with the server code/models but I dont think its the right direction :/
    – berkeleybross
    Nov 12 '18 at 23:02










  • I see. The structure is right. Just double confirm.
    – wannadream
    Nov 12 '18 at 23:11
















0












0








0







I'm trying to write an overload for HttpClient.PutAsJsonAsync<T> which allows headers to be set on each request. Looking at the source code for that method, it creates an ObjectContent<T> internally, which I tried to emulate below.



However, my target server always receives a null body when binding (using [FromBody] Form form).



If I change the request to be a StringContent and manually serialize the body, the request succeeds! What could be the difference between the two implementations?



Even more strangely, if I call await content.ReadAsStringAsync() before SendAsync then it also works!



My thoughts are it could be either:




  • The JSON is being serialized differently (But I'm not configuring any Json.NET globals anywhere.) The obvious problem would be different capitalization, but as far as I know, JSON.net is case insensitive when parsing, and my server will be using JSON.net

  • I'm not awaiting something and the content is being sent before it serialises fully. I can't spot any un-awaited Tasks in my code though, and the entrypoint is a standard async Main()


My environments is:




  • Client is a simple netcoreapp2.1 exe, running in a microsoft/dotnet:2.1-sdk-alpine docker image.

  • Server is a .NET framework MVC5 WebApi, with many similar APIs which other clients can call fine


The code below is a trimmed down version of what my client is calling.



public static class UserUpdater
{
public static async Task UpdateAsync(List<string> users)
{
using (var client = new HttpClient())
{
var form = new { Users = users };
var headers = new Dictionary<string, string>
{
["Authorization"] = "Bearer " // Real JWT here
};

var response = await client.PutAsJsonAsync(new Uri("http://localhost/api/app/v1/gateway-users"), form, headers);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException("Response status code does not indicate success: " +
response.StatusCode + "nn" +
await response.Content.ReadAsStringAsync());
}
}
}

public static async Task<HttpResponseMessage> PutAsJsonAsync<T>(this HttpClient client, Uri requestUri, T value,
IEnumerable<KeyValuePair<string, string>> headers, CancellationToken cancellationToken = default)
{
var content = new ObjectContent<T>(value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null);
////var content = new StringContent(JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json"); // This works fine
var message = MakeRequestMessage(HttpMethod.Put, requestUri, headers, content);
return await client.SendAsync(message, cancellationToken);
}

private static HttpRequestMessage MakeRequestMessage(
[NotNull] HttpMethod method,
[NotNull] Uri requestUri,
[CanBeNull] IEnumerable<KeyValuePair<string, string>> headers,
[CanBeNull] HttpContent content = null)
{
var message = new HttpRequestMessage(method, requestUri);

if (headers != null)
{
foreach (var header in headers)
{
message.Headers.Add(header.Key, header.Value);
}
}

if (content != null)
{
message.Content = content;
}

return message;
}


If it helps, this is the server side (MVC5 WebApi):



    [Route("")]
[ValidateModel]
public IHttpActionResult Put([Required] [FromBody] GatewayUserCollectionForm form)
{
Ensure.NotNull(form, nameof(form)); // This throws since form is null

this.manager.UpdateGatewayUsers(form.Users.ToList());
return this.Ok();
}

public class GatewayUserCollectionForm
{
[Required]
public List<string> Users { get; set; }
}









share|improve this question















I'm trying to write an overload for HttpClient.PutAsJsonAsync<T> which allows headers to be set on each request. Looking at the source code for that method, it creates an ObjectContent<T> internally, which I tried to emulate below.



However, my target server always receives a null body when binding (using [FromBody] Form form).



If I change the request to be a StringContent and manually serialize the body, the request succeeds! What could be the difference between the two implementations?



Even more strangely, if I call await content.ReadAsStringAsync() before SendAsync then it also works!



My thoughts are it could be either:




  • The JSON is being serialized differently (But I'm not configuring any Json.NET globals anywhere.) The obvious problem would be different capitalization, but as far as I know, JSON.net is case insensitive when parsing, and my server will be using JSON.net

  • I'm not awaiting something and the content is being sent before it serialises fully. I can't spot any un-awaited Tasks in my code though, and the entrypoint is a standard async Main()


My environments is:




  • Client is a simple netcoreapp2.1 exe, running in a microsoft/dotnet:2.1-sdk-alpine docker image.

  • Server is a .NET framework MVC5 WebApi, with many similar APIs which other clients can call fine


The code below is a trimmed down version of what my client is calling.



public static class UserUpdater
{
public static async Task UpdateAsync(List<string> users)
{
using (var client = new HttpClient())
{
var form = new { Users = users };
var headers = new Dictionary<string, string>
{
["Authorization"] = "Bearer " // Real JWT here
};

var response = await client.PutAsJsonAsync(new Uri("http://localhost/api/app/v1/gateway-users"), form, headers);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException("Response status code does not indicate success: " +
response.StatusCode + "nn" +
await response.Content.ReadAsStringAsync());
}
}
}

public static async Task<HttpResponseMessage> PutAsJsonAsync<T>(this HttpClient client, Uri requestUri, T value,
IEnumerable<KeyValuePair<string, string>> headers, CancellationToken cancellationToken = default)
{
var content = new ObjectContent<T>(value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null);
////var content = new StringContent(JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json"); // This works fine
var message = MakeRequestMessage(HttpMethod.Put, requestUri, headers, content);
return await client.SendAsync(message, cancellationToken);
}

private static HttpRequestMessage MakeRequestMessage(
[NotNull] HttpMethod method,
[NotNull] Uri requestUri,
[CanBeNull] IEnumerable<KeyValuePair<string, string>> headers,
[CanBeNull] HttpContent content = null)
{
var message = new HttpRequestMessage(method, requestUri);

if (headers != null)
{
foreach (var header in headers)
{
message.Headers.Add(header.Key, header.Value);
}
}

if (content != null)
{
message.Content = content;
}

return message;
}


If it helps, this is the server side (MVC5 WebApi):



    [Route("")]
[ValidateModel]
public IHttpActionResult Put([Required] [FromBody] GatewayUserCollectionForm form)
{
Ensure.NotNull(form, nameof(form)); // This throws since form is null

this.manager.UpdateGatewayUsers(form.Users.ToList());
return this.Ok();
}

public class GatewayUserCollectionForm
{
[Required]
public List<string> Users { get; set; }
}






c# dotnet-httpclient






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 12 '18 at 23:04







berkeleybross

















asked Nov 12 '18 at 22:24









berkeleybrossberkeleybross

609921




609921












  • Did you make sure that your api is returning 'application/json'?
    – wannadream
    Nov 12 '18 at 22:48










  • @wannadream It would normally, but it throws a NullReferenceException since it reads the bound form (which is null). It therefore returns a HTTP 500 with no body
    – berkeleybross
    Nov 12 '18 at 22:50












  • Can you change this line to this? var response = await client.PutAsJsonAsync(new Uri("localhost/api/app/v1/gateway-users"), users, headers); I do not see it is necessary to wrap it with an anonymous object.
    – wannadream
    Nov 12 '18 at 22:56












  • @wannadream unfortunately not, the server is expecting an object which contains a list. I'll update with the server code/models but I dont think its the right direction :/
    – berkeleybross
    Nov 12 '18 at 23:02










  • I see. The structure is right. Just double confirm.
    – wannadream
    Nov 12 '18 at 23:11




















  • Did you make sure that your api is returning 'application/json'?
    – wannadream
    Nov 12 '18 at 22:48










  • @wannadream It would normally, but it throws a NullReferenceException since it reads the bound form (which is null). It therefore returns a HTTP 500 with no body
    – berkeleybross
    Nov 12 '18 at 22:50












  • Can you change this line to this? var response = await client.PutAsJsonAsync(new Uri("localhost/api/app/v1/gateway-users"), users, headers); I do not see it is necessary to wrap it with an anonymous object.
    – wannadream
    Nov 12 '18 at 22:56












  • @wannadream unfortunately not, the server is expecting an object which contains a list. I'll update with the server code/models but I dont think its the right direction :/
    – berkeleybross
    Nov 12 '18 at 23:02










  • I see. The structure is right. Just double confirm.
    – wannadream
    Nov 12 '18 at 23:11


















Did you make sure that your api is returning 'application/json'?
– wannadream
Nov 12 '18 at 22:48




Did you make sure that your api is returning 'application/json'?
– wannadream
Nov 12 '18 at 22:48












@wannadream It would normally, but it throws a NullReferenceException since it reads the bound form (which is null). It therefore returns a HTTP 500 with no body
– berkeleybross
Nov 12 '18 at 22:50






@wannadream It would normally, but it throws a NullReferenceException since it reads the bound form (which is null). It therefore returns a HTTP 500 with no body
– berkeleybross
Nov 12 '18 at 22:50














Can you change this line to this? var response = await client.PutAsJsonAsync(new Uri("localhost/api/app/v1/gateway-users"), users, headers); I do not see it is necessary to wrap it with an anonymous object.
– wannadream
Nov 12 '18 at 22:56






Can you change this line to this? var response = await client.PutAsJsonAsync(new Uri("localhost/api/app/v1/gateway-users"), users, headers); I do not see it is necessary to wrap it with an anonymous object.
– wannadream
Nov 12 '18 at 22:56














@wannadream unfortunately not, the server is expecting an object which contains a list. I'll update with the server code/models but I dont think its the right direction :/
– berkeleybross
Nov 12 '18 at 23:02




@wannadream unfortunately not, the server is expecting an object which contains a list. I'll update with the server code/models but I dont think its the right direction :/
– berkeleybross
Nov 12 '18 at 23:02












I see. The structure is right. Just double confirm.
– wannadream
Nov 12 '18 at 23:11






I see. The structure is right. Just double confirm.
– wannadream
Nov 12 '18 at 23:11














0






active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270970%2fobjectcontent-not-working-but-stringcontent-does%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270970%2fobjectcontent-not-working-but-stringcontent-does%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Florida Star v. B. J. F.

Danny Elfman

Lugert, Oklahoma