How to deserialize parameter from json in webapi controller - c#

seems like I have the most basic problem, yet I cannot find the documentation I need to solve it.
I have an mvc webapi controller:
public class TestController : ApiController
{
[HttpGet]
public MyClass Other([FromUri]MyClass id)
{
id.Value++;
return id;
}
}
public class MyClass
{
public int Value {get;set;}
}
which I am executing from a HttpClient:
using (var client = new System.Net.Http.HttpClient())
{
client.BaseAddress = new System.Uri("http://localhost:31573/api/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var obj = new MyClass { Value = 3 };
var data = JsonConvert.SerializeObject(obj);
StringContent queryString = new StringContent(data, Encoding.UTF8, "application/json");
var paramsValue = queryString.ReadAsStringAsync().Result;
var response = client.GetAsync("Test/?id="+ paramsValue).Result;
var textResponse = response.Content.ReadAsStringAsync().Result;
var result = JsonConvert.DeserializeObject<MyClass>(textResponse);
}
The problem is that the parameter id which is received by the controller is a default instance of MyClass (i.e. Value = 0). If I change the prototype of the method to accept a string:
[HttpGet]
public MyClass Other([FromUri]string id)
{
var val = JsonConvert.DeserializeObject<MyClass>(id);
val.Value++;
return val;
}
it all works fine, but I would rather not have to manually do the deserialization in every controller method.
I have tried many combinations of how I create the query string, so far having no luck.
It appears that the data is getting to the webApi correctly, but the deserialization is not happening, so I suspect that I have not configure the webApi correctly to use json form the request parameters.
my webapiconfig looks like:
public static void Register(HttpConfiguration config)
{
if (config == null)
throw new ArgumentNullException(nameof(config));
// Web API configuration and services
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
}
so its returning json correctly, but incoming parameters are not deserialized correctly.
Can anyone help??

Thanks to the help from #Mostafizur Rahman, I came to the conclusion that a get method was not appropriate here, So I have changed to a Post method and pass the data in the body:
public class TestController : ApiController
{
public MyClass PostMethod([FromBody]MyClass id)
{
id.Value++;
return id;
}
}
public class MyClass
{
public int Value {get;set;}
}
with the client side becoming:
using (var client = new System.Net.Http.HttpClient())
{
client.BaseAddress = new System.Uri("http://localhost:31573/api/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var obj = new MyClass { Value = 3 };
var data = JsonConvert.SerializeObject(obj);
StringContent queryString = new StringContent(data, Encoding.UTF8, "application/json");
var paramsValue = queryString.ReadAsStringAsync().Result;
var response = client.PostAsync("Test/PostMethod", queryString).Result;
var textResponse = response.Content.ReadAsStringAsync().Result;
var result = JsonConvert.DeserializeObject<MyClass>(textResponse);
}

this is the querystring that you'll need to form -
http://localhost:61817/api/Test?Value=3
Then the code changes like -
var response = client.GetAsync("Test?Value="+ obj.Value).Result;

Related

API POST call from Console Application

How to do the REST API POST Call from the console Application ?
I want to pass the class from the Console application to the REST API. My below code is working if I have to do the GET call but not for the POST. It is hitting the API but in the Parameter it is not passing anything.
API
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
//public void Post([FromBody]string value)
//{
//}
public void Post([FromBody]Student value)
{
}
}
Console Application
static async Task CallWebAPIAsync()
{
var student = new Student() { Id = 1, Name = "Steve" };
using (var client = new HttpClient())
{
//Send HTTP requests from here.
client.BaseAddress = new Uri("http://localhost:58847/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync("api/values", student);
if (response.IsSuccessStatusCode)
{
}
else
{
Console.WriteLine("Internal server Error");
}
}
}
The Same is working if I call from fiddler.
User-Agent: Fiddler
Content-Length: 31
Host: localhost:58847
Content-Type: application/json
Request Body:
{
"Id":"1",
"Name":"Rohit"
}
This is working for me.
public async Task CallWebAPIAsync()
{
var student = "{'Id':'1','Name':'Steve'}";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:58847/");
var response = await client.PostAsync("api/values", new StringContent(student, Encoding.UTF8, "application/json"));
if (response != null)
{
Console.WriteLine(response.ToString());
}
}
You are not serializing the student object.
You can try to send a StringContent
StringContent sc = new StringContent(Student)
HttpResponseMessage response = await client.PostAsJsonAsync("api/values", sc);
if this doesn't work (a long time I used StringContent).
Use NewtonSoft sterilizer
string output = JsonConvert.SerializeObject(product);
HttpResponseMessage response = await client.PostAsJsonAsync("api/values", output);
To be honest I don't know. It seems like your StringContent did not sterilize it to UTF8 which your restful API is trying to do by default. However, your console application should also do that by default.
The issue seemed to be that the restful API could not bind the byte data and therefor not assign the data to your class Student in the restful API.
What you can try to do is add following code before you make your post to API:
var encoding = System.Text.Encoding.Default;
It will tell you what is your default encoding type. It could be that UTF8 is not the default encoding for some reason.

Unable to Call Web Api from Winform (Bad Request ERROR)

I am trying to call An API from winform Application, But I am getting the bad request error. I think I couldn't format the valid string for my post request.
Edit Adding driverDayLogModel Class Code
driveDayLogModel.cs
public class driverDayLogModel
{
public int driverId { get; set; }
public DateTime dayStartTime { get; set; }
}
API calling code
driverDayLogModel driverDayLog = new driverDayLogModel
{
driverId = Convert.ToInt32(txtId.Text),
dayStartTime = DateTime.Now,
};
var js = new JavaScriptSerializer();
string json = JsonConvert.SerializeObject(driverDayLog);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://127.0.0.1:54314/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PostAsJsonAsync("api/driverdaylogs", json).Result;
if (response.IsSuccessStatusCode)
{
// Some Stuff
}
else
{
MessageBox.Show("Error Code" +
response.StatusCode + " : Message - " + response.ReasonPhrase);
}
}
I am getting Json As "{\"driverId\":1,\"dayStartTime\":\"2019-03-03T21:44:15.1912313+05:00\"}"
Which seems a not valid json for the API, when I try with same json on postman it also resulting 400 Bad Request Error, But it must be noted that When I call API with the Json {"driverId":1,"dayStartTime":"2019-03-03T21:44:15.1912313+05:00"} My request goes succesful to API, and I got expected response in POSTMAN. I am unable to convert the json into acceptable format for the webapi in winforms
Following is the code for Webapi.
public IHttpActionResult PostDayLogs([FromBody]driverDayLogModel log)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
var driverDayLog = new driverDailyLogs();
driverDayLog.dayEndTime = DateTime.Now;
driverDayLog.dayStartTime = log.dayStartTime;
driverDayLog.driverId = log.driverId;
var max = db.driverDailyLogs.OrderByDescending(r => r.id).FirstOrDefault();
driverDayLog.id = max == null ? 1 : max.id + 1;
driverDayLog.logDate = DateTime.Now.Date;
db.driverDailyLogs.Add(driverDayLog);
db.SaveChanges();
return Ok(driverDayLog);
}
In WebApiConfig I have the following code
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Formatters.JsonFormatter
.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Please guide me in this regard.
Using HttpClient you don't have to serialize your object to Json before sending it to the Web Api. The HttpClient will take care of the serialization for you.
Edit (removed previous code that was apparently not working)
Instead of sending the Json in PostAsJsonAsync, simply send the object.
driverDayLogModel driverDayLog = new driverDayLogModel
{
driverId = Convert.ToInt32(txtId.Text),
dayStartTime = DateTime.Now,
};
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://127.0.0.1:54314/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// simply put the object as the second parameter instead of a Json string
var response = client.PostAsJsonAsync("api/driverdaylogs", driverDayLog).Result;
if (response.IsSuccessStatusCode)
{
// Some Stuff
}
else
{
MessageBox.Show("Error Code" +
response.StatusCode + " : Message - " + response.ReasonPhrase);
}
}
As a side note: Using .Result will block your code, so the recommended way is using await and async, but that is outside of the scope of this question/answer.

MVC Api Controller Serielized parameters

I am doing an MVC 5 Application, and I am calling a API controller method that is in another Solution.
I am using HttpClient(). and I am calling PostAsJsonAsync with some parameters, an instance of a class.
It looks like this.
string apiUrl = "localhost:8080/api/";
ContactWF contactWF = new contactWF();
contactWF.contact_id=0;
contactWF.UserOrigin_id=20006
contactWF.ProcessState_id=2;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync(apiUrl + "Contact/Method", contactWF);
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<int>().Result;
}
}
My API controller method is like this.
[ActionName("Method")]
[HttpGet]
public int Method([FromBody] ContactWF userwf)
{
return 10;
}
It Works fine...
My problem is when I try Serialized the parameter class instance
I replace line
HttpResponseMessage response = await client.PostAsJsonAsync(apiUrl + "Contact/Method", contactWF);
with this one
string jsonData = JsonConvert.SerializeObject(contactWF);
HttpResponseMessage response = client.PostAsJsonAsync("api/Contact/Method", jsonData).Result;
I've got an Error:405...
It looks like the Json string it is not recognize as a Parameter.
My Json string looks like this.
"{\"Contact_id\":0,\"Description\":null,\"ProcessState_id\":2,\"Type_id\":0,\"Object_id\":0,\"Parent_id\":null}"
that is ContactWD class converter to json.
What´s wrong?
Method PostAsJsonAsync serialize parameter object himself, so it serialized your json string again.
If you need serialize object himself for some reason, then use method HttpClient.PostAsync
string jsonData = JsonConvert.SerializeObject(contactWF);
var stringContent = new StringContent(jsonData, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync("api/Filler/CountMensajeByUser", stringContent);
Change verb to HttpPost in your api controller
[ActionName("Method")]
[HttpPost]
public int Method([FromBody] ContactWF userwf)
{
return 10;
}
Update
You don't need to serialize object in PostAsJsonAsync
HttpResponseMessage response = client.PostAsJsonAsync("api/Contact/Method", contactWF).Result;
Take a look at sample code from microsoft
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/testing
internal class NewIdeaDto
{
public NewIdeaDto(string name, string description, int sessionId)
{
Name = name;
Description = description;
SessionId = sessionId;
}
public string Name { get; set; }
public string Description { get; set; }
public int SessionId { get; set; }
}
//Arrange
var newIdea = new NewIdeaDto("Name", "", 1);
// Act
var response = await _client.PostAsJsonAsync("/api/ideas/create", newIdea);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);

MVC controller post entire model to remote api action

I have an API I would like to call from my front end MVC site. These two applications run on separate servers on the same network.
The API Controller has functions similar to:
[AllowCrossSiteJson]
public class VerifyMyModelController : ApiController
{
[HttpPost]
public MyResponse Post(MyModel model)
{
return MyHelper.VerifyMyModel(model);
}
[HttpPost]
public async Task<MyResponse> PostAsync(MyModel model)
{
return await MyHelper.VerifyMyModel(model);
}
// ... Gets below as well
}
Where MyHelper performs model verfication, DB lookups etc... and returns a common response object with response code, database id etc
My front end MVC site has a form the user fills out, this data gets posted to the local controller which I would like to forward on to the API. The API application is not accessible to the public, so I cannot post directly to it using AJAX etc. It must come from the sites controller.
I have tried the following but get a 500 internal server error as a response
[HttpPost]
public async Task<MyResponse> VerifyAsync(MyModel model)
{
var MyServer = ConfigurationManager.AppSettings["MyServer"];
var json = Newtonsoft.Json.JsonConvert.SerializeObject(model);
var requestUri = string.Format(#"http://{0}/api/VerifyMyModel/", MyServer);
using (var c = new HttpClient())
{
var response = await c.PostAsJsonAsync(requestUri, json);
}
...
}
The var response contains the error message response 500.
I have also tried using a query string:
public string GetQueryString(object obj)
{
var properties = from p in obj.GetType().GetProperties()
where p.GetValue(obj, null) != null
select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString());
return String.Join("&", properties.ToArray());
}
[HttpPost]
public async Task<MyResponse> VerifyAsync(MyModel model)
{
var MyServer = ConfigurationManager.AppSettings["MyServer"];
string queryString = GetQueryString(model);
var requestUri = string.Format(#"http://{0}/api/VerifyMyModel/?{1}", MyServer, queryString);
using (var c = new HttpClient()){
var response = await c.GetAsync(requestUri); // API Also has GET methods
}
}
But the querystring method returns a 405 method not allowed response.
The MyModel is part of a shared class library with common models in it and is included in both applications.
Is there a better way of posting the entire model to the remote api action?
Thanks.
*Edit
RouteConfig of API:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
I added the following to the HomeController of the API's MVC site, to test it and I receive back the expected result, without error:
public async Task<ActionResult> TestVerifyMyModel(MyModel model)
{
var api = new VerifyMyModelController();
var res = await api.PostAsync(model);
return Json(res, JsonRequestBehavior.AllowGet);
}
So I know that the PostAsync Action of the controller works.. I just cant get it to work when called remotely.
I also enabled Failed Request Tracing on the server and have uploaded the generated XML file. It doesn't mean anything to me but thought it might help.
The posted route config looks more like your MVC route config than a Web Api one. But if it is the Web Api config, then shouldn't you be adding the ActionName to your url.
[HttpPost]
public async Task<MyResponse> VerifyAsync(MyModel model)
{
var MyServer = ConfigurationManager.AppSettings["MyServer"];
var json = Newtonsoft.Json.JsonConvert.SerializeObject(model);
var requestUri = string.Format(#"http://{0}/api/VerifyMyModel/PostAsync", MyServer);
using (var c = new HttpClient())
{
var response = await c.PostAsJsonAsync(requestUri, json);
}
...
}
Update: Sample code to retrieve Model from HttpClient response
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ConfigurationManager.AppSettings["MyServer"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/VerifyMyModel/PostAsync");
if (response.IsSuccessStatusCode)
{
var myResponseModel = await response.Content.ReadAsAsync<MyResponseModel>();
}
}

C# Sending an object through HttpClient to a Web API

I have a WindowsForm application and i want to send an List<> to the Web API
here is my code in the windows form app:
Uri uri = new Uri("http://localhost/test/api/v1/name/testcontroller/");
HttpClient client = new HttpClient();
client.BaseAddress = uri;
var mediaType = new MediaTypeHeaderValue("application/json");
var jsonFormatter = new JsonMediaTypeFormatter();
HttpContent content = new ObjectContent<List<TermbaseFile>>(termbaseList, jsonFormatter);
HttpResponseMessage responseMessage = client.PostAsync(uri, content).Result;
What should i put in the controller-method to get the List?
You need to implement a Post action that expects a list of that particular object type, or more specifically, an object which has the same properties e.g.
public class TermbaseFilePostDto
{
// relevant properties go here
}
public class TestController : ApiController
{
public HttpResponseMessage Post(List<TermbaseFileDto> list)
{
...
}
}

Categories