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.
Related
I need to save the changes I make in my model through API call in my database. I have checked my API is working fine when I am running it individually on Web. But its giving me an error StatusCode: 405, ReasonPhrase: 'Method Not Allowed'. I am trying to send and object and trying to see whether the request made was completed or not. When I am trying to debug it, it is not sending hit on my API controller.
Here is my model class:
public class Customer
{
[Required]
public Guid CustomerId { get; set; }
public int Age { get; set; }
public int Phone { get; set; }
}
PUT Method in API:
[HttpPut]
[Route("api/[controller]/{customer}")]
public IActionResult EditCustomer(Customer customer)
{
var cust = _customerData.EditCustomer(customer);
if (cust == string.Empty)
{
return Ok();
}
else
{
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
The method I am using in project to call API:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiBaseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")
);
var sum = await client.PutAsJsonAsync("api/Customer/", customer);
if (sum.StatusCode == System.Net.HttpStatusCode.OK)
{
return RedirectToActionPermanent(actionName: "SingIn");
}
else
{
TempData["msg"] = "There is an error";
return View();
}
where baseaddress= {https://localhost:44398/}
EditCustomer Method
public string EditCustomer(Customer customer)
{
try
{
var pro = _customerContext.Customer.Where(e => e.CustomerId == customer.CustomerId).FirstOrDefault();
pro.Age = customer.Age;
pro.Phone = customer.Phone;
pro.Name = customer.Name;
_customerContext.Entry(pro).State = EntityState.Modified;
_customerContext.SaveChanges();
}
catch(Exception e)
{
return e.Message;
}
return string.Empty;
}
You need to fix your action route by removing {Customer}, since you send customer in request body, not as a route value
[Route("~/api/Customer")]
and request
var sum = await client.PutAsJsonAsync("/api/Customer", customer);
or better fix the acttion route name to meaningfull
[Route("~/api/EditCustomer")]
and
var sum = await client.PutAsJsonAsync("/api/EditCustomer", customer);
AsJsonAsync sometimes causes problems
try this code
var json = JsonSerializer.Serialize(customer);
//or if you are using Newtonsoft
var json = JsonConvert.SerializeObject(customer);
var contentData = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PutAsync("/api/Customer", contentData);
if (response.IsSuccessStatusCode)
return RedirectToActionPermanent("SingIn");
else
{
TempData["msg"] = "There is an error";
return View();
}
but IMHO I would prefer to use
client.PostAsync("/api/EditCustomer", contentData);
instead of Put.
and added [FromBody] to action
[HttpPost("~/api/EditCustomer")]
public IActionResult EditCustomer([FromBody] Customer customer)
I am no pro in web APIs but I suspect it could be due to the fact that the API expects customer to be in request URL.
Try and change the API route to [Route("api/[controller]")]
This could've been a comment but I don't have enough reputation :)
I have trouble trying to create an integration test for my post call that accepts a viewmodel that has amongst other values, an IFormFile, which makes this call from an application/json to a multipart/form-data
My IntegrationSetup class
protected static IFormFile GetFormFile()
{
byte[] bytes = Encoding.UTF8.GetBytes("test;test;");
var file = new FormFile(
baseStream: new MemoryStream(bytes),
baseStreamOffset: 0,
length: bytes.Length,
name: "Data",
fileName: "dummy.csv"
)
{
Headers = new HeaderDictionary(),
ContentType = "text/csv"
};
return file;
}
My Test Method
public async Task CreateAsync_ShouldReturnId()
{
//Arrange
using var content = new MultipartFormDataContent();
var stringContent = new StringContent(
JsonConvert.SerializeObject(new CreateArticleViewmodel
{
Title = "viewModel.Title",
SmallParagraph = "viewModel.SmallParagraph",
Url = "viewModel.Url",
Image = GetFormFile()
}),
Encoding.UTF8,
"application/json");
stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
content.Add(stringContent, "json");
//Act
var response = await httpClient.PostAsync($"{Url}", content);
//Assert
response.StatusCode.ShouldBe(HttpStatusCode.OK);
int id = int.Parse(await response.Content.ReadAsStringAsync());
id.ShouldBeGreaterThan(0);
}
My Controller Method
[HttpPost]
public async Task<IActionResult> CreateArticleAsync([FromForm] CreateArticleViewmodel viewModel)
{
var id = await _service.CreateAsync(viewModel).ConfigureAwait(false);
if (id > 0)
return Ok(id);
return BadRequest();
}
It throws a BadRequest without getting inside the method.
The way you are posting the request contents to the API, in your code, is not correct.
When the API expects a FileInfo in the request payload, posting JSON content never works. You need to send the payload as MultipartFormData and not as JSON.
Consider following example.
This is a an API endpoint which expects and model with FileInfo in it as payload.
[HttpPost]
public IActionResult Upload([FromForm] MyData myData)
{
if (myData.File != null)
{
return Ok("File received");
}
else
{
return BadRequest("File no provided");
}
}
public class MyData
{
public int Id { get; set; }
public string Title { get; set; }
// Below property is used for getting file from client to the server.
public IFormFile File { get; set; }
}
This is pretty much the same API as yours.
Following is the client code which calls the above API with file and other model properties.
var apiURL = "http://localhost:50492/home/upload";
const string filename = "D:\\samplefile.docx";
HttpClient _client = new HttpClient();
// Instead of JSON body, multipart form data will be sent as request body.
var httpContent = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(File.ReadAllBytes(filename));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
// Add File property with file content
httpContent.Add(fileContent, "file", filename);
// Add id property with its value
httpContent.Add(new StringContent("789"), "id");
// Add title property with its value.
httpContent.Add(new StringContent("Some title value"), "title");
// send POST request.
var response = await _client.PostAsync(apiURL, httpContent);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
// output the response content to the console.
Console.WriteLine(responseContent);
The client code is running from a Console application. So when I run this, the expectation is to get File received message in the console and I am getting that message.
Following is the screen capture of the model content at the API end while debugging it.
And if I am calling this API from postman, it would look like following.
I hope this will help you solve your issue.
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.
When I try to debug the application by calling like
http://localhost:5/api/GetEmployeesDEV
It returns error like
No HTTP resource was found that matches the request URI 'http://localhost:57764/api/GetEmployeesDEV'. No action was found on the controller 'GetEmployeesDEV' that matches the request.
The ASP.NET Web API which makes a Call to the Odata endpoint and returns the response received by the call. And I have the below code for the Controller
public class GetEmployeesDEVController : ApiController
{
[HttpGet]
private async Task<EmployeeDTO.RootObject> Get()
{
string userName_Core = ConfigurationManager.AppSettings["core_Username"];
string password_Core = ConfigurationManager.AppSettings["core_Password"];
string BaseURL_Core = ConfigurationManager.AppSettings["BaseURL_Core"];
var byteArray_Core = Encoding.ASCII.GetBytes(userName_Core + ":" + password_Core);
EmployeeDTO.RootObject returnObj = new EmployeeDTO.RootObject();
try
{
// GET
using (var client_Core = new HttpClient())
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
Uri uri = new Uri(BaseURL_Core);
client_Core.BaseAddress = uri;
client_Core.DefaultRequestHeaders.Accept.Clear();
client_Core.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client_Core.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray_Core));
string core_URL = BaseURL_Core;
var response = client_Core.GetAsync(core_URL).Result;
var responsedata = await response.Content.ReadAsStringAsync();
returnObj = JsonConvert.DeserializeObject<EmployeeDTO.RootObject>(responsedata);
}
}
catch (Exception ex)
{
throw ex;
}
return returnObj;
}
Not sure if I am missing something here. I didnot change anything with RouteConfig file
Please change the scope of the function
From private
private async Task<EmployeeDTO.RootObject> Get()
To public
public async Task<EmployeeDTO.RootObject> Get()
Action need to be public in order to be available for routing.
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;