0

I had this working before and now I do not. But, I cannot see what I have done to make it not work?

This is my MVC API Controller:

[HttpPost]
public Models.CustomerAddress Add(string Customer)
{
    //the customer is a json string
}

This is my C# desktop Client:

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri(Shared.URL);
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Shared.HeaderType));
    string jsonInput = JsonConvert.SerializeObject(customer);
    HttpContent contentPost = new StringContent(jsonInput, Encoding.UTF8, "application/json");
   var response = client.PostAsync(string.Format("http://myuri/api/Customer/Add?Customer="), contentPost).Result;
    response.EnsureSuccessStatusCode();
    string json = await response.Content.ReadAsStringAsync();
    return JsonConvert.DeserializeObject<Model.CustomerAddress>(json);
}

The customer object is definitely populated...

This does call my api but the Customer string is null??

9
  • Is the customer object just a string, or something more complex? Also, why are you passing an empty Customer query string parameter? That may be confusing the model binder. Commented Nov 9, 2015 at 20:21
  • Hi, the Customer Object has fields like surname and firstname etc. If I point a breakpoint on it the object is populated. I am not sure what you mean by empty Customer string. I thought this was the way to pass in a JSON object? Commented Nov 9, 2015 at 20:22
  • @closer why? This question is a programming question. What is not programmatic about it? Confused? Commented Nov 9, 2015 at 20:24
  • Are you sure that controller is the right one? The URL would lead me to think it should be Customer rather than CustomerAddress Commented Nov 9, 2015 at 20:27
  • @JBKing Yes. I set a break-point and it enters it.. Commented Nov 9, 2015 at 20:28

3 Answers 3

3

Using [FromUri]

To force Web API to read a complex type from the URI, add the [FromUri] attribute to the parameter. The following example defines a GeoPoint type, along with a controller method that gets the GeoPoint from the URI.

public class GeoPoint
{
    public double Latitude { get; set; } 
    public double Longitude { get; set; }
}

public ValuesController : ApiController
{
    public HttpResponseMessage Post([FromUri] GeoPoint location) { ... }
}

The client can put the Latitude and Longitude values in the query string and Web API will use them to construct a GeoPoint. For example:

http://localhost/api/values/?Latitude=47.678558&Longitude=-122.130989

ASP.NET WebAPI: Details and examples

Sign up to request clarification or add additional context in comments.

1 Comment

Fantatsic - that works :) I can accept answer in 4 minutes. Thanks
1

There is definitely a mismatch between the client and the server. Your URI specifies a parameter with a name Customer which would match the method's parameter, but you're not supplying any value to that parameter. There is nothing after the = sign.

The way you pass the serialized object in your client code, it would actually get to the server as a body of the request. In order to get it as a parameter in your method you should actually use [FromBody] attribute and remove the &Customer= portion from your URL.

[HttpPost]
public Models.CustomerAddress Add([FromBody]string Customer)
{
    //the customer is a json string
}

And this would be the client:

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri(Shared.URL);
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Shared.HeaderType));
    string jsonInput = JsonConvert.SerializeObject(customer);
    HttpContent contentPost = new StringContent(jsonInput, Encoding.UTF8, "application/json");
   var response = client.PostAsync("http://myuri/api/Customer/Add", contentPost).Result;
    response.EnsureSuccessStatusCode();
    string json = await response.Content.ReadAsStringAsync();
    return JsonConvert.DeserializeObject<Model.CustomerAddress>(json);
}

This would be a better way to pass data into a Post method because you would not need to encode it to satisfy the requirements of the URI format. And sending data in the body of the request can accommodate larger payloads as well.

5 Comments

@AndrewSimpson - BTW, I've just noticed that you're using PostAsync().Result, should change that to await, the same as two lines below
I would like to check out venerik's answer as well. But, I ahve to leave my PC for a few hours. I will come back to this and adjust accepted answer after I compared all these answers. thanks to everyone!
Hi, my question has evolved which is not a good thing for people trying to help me. Am i to understand the choice is between using [FromBody] when passing in a string (where the string is a complex object) or no [FromBody] when passing in a complex object?
Passing data in the body of the request is just another option for POST. GET requests cannot have a body so all the parameters have to be passed through the URI. Also, my personal opinion is that classes that derive from HttpContent (like the StringContent in your code) provide more flexible API than UriBuilder.
hi, I have gone the way that you have recommended. I have to leave the original accepted answer as it was in relation to passing json via a uri. I prefer your approach. thanks
0

You can simply change the controller to:

[HttpPost]
public Models.CustomerAddress Add(Customer customer)
{
    // customer gets instantiated by the model binder
}

The client can now post a request like this:

    var jsonCustomer = JsonConvert.SerializeObject(customer);
    var contentPost = new StringContent(jsonCustomer, Encoding.UTF8, "application/json");
    var response = client.PostAsync("http://myuri/api/Customer/Add", contentPost).Result;

2 Comments

another great example!
I like the way this is done. A lot cleaner. But would i not also need to include the [FromBody] tag?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.