ObjectContent not working but StringContent does
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 amicrosoft/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
|
show 3 more comments
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 amicrosoft/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
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
|
show 3 more comments
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 amicrosoft/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
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 amicrosoft/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
c# dotnet-httpclient
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
|
show 3 more comments
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
|
show 3 more comments
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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