Web API HttpPost method always receives GET - c#

I have a web api controller with a method
[HttpPost]
[Authorize]
[Route("[action]")]
public async Task<IActionResult> Authenticate(HttpRequestMessage msg)
{
//msg.Method is always GET
// msg.Content is null
}
I call it from my desktop application like this:
HttpClient client = new HttpClient(new HttpClientHandler(){AllowAutoRedirect = false});
foreach (var header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
await client.PostAsync(requestUrl, content);
But in the web api method the property msg.Method is always GET and msg.Content is null
Is it sort of redirect or another intentional behavior that every request turns into GET?
I tried to change it to
[Authorize]
[HttpPost]
[Route("[action]")]
public async Task<IActionResult> C2dSend([FromBody]string request)
{
return Ok();
}
but got 'BadRequest' after call.
The only thing that works is to replace string with dynamic:
[Authorize]
[HttpPost]
[Route("[action]")]
public async Task<IActionResult> C2dSend([FromBody]dynamic request)
{
return Ok();
}

Only POST requests are routed to your action. You just looking at the wrong place - you are trying to bind body of your request to HttpRequestMessage instance. Obviously, you are passing something else here.
If you are looking for request details, use the Request property of controller. It is populated from contoller context which is passed to the controller during activation:
[Authorize]
[HttpPost]
[Route("[action]")]
public async Task<IActionResult> C2dSend()
{
// Request.Method is POST here
return Ok();
}
But you don't need to use it to get request content. Let Asp.Net do this work for you - declare a model class which has the same properties as the serialized object which you are sending and model binder will do deserialization work for you:
[Authorize]
[HttpPost]
[Route("[action]")]
public async Task<IActionResult> C2dSend(YourModel blah)
{
// model will be populated automatically from request body
return Ok();
}
Note: There is an easier way to send json requests. You can use PostAsJsonAsync extension from System.Net.Http.Formatting.Extension

Related

Unable to call web api via httpclient

I have a controller with the following route:
[HttpGet("/{id}/studentrank")]
public async Task<ActionResult> GetStudentRank(Guid id){
...
}
Note, the decorator over the class is: [Route("api/[controller]")]
so it should be called by: api/student/{guid here}/studentrank
This works fine in swagger. However when I call it as follow, I get an internal server error and does not even break in the controller:
var response = await HttpClient.GetAsync($"/api/student/{id}/studentrank");
Any idea of what could be missing?
The HttpGet does not contain the route parameter. Assuming the method is in the StudentController class you should do it like this:
[HttpGet]
[Route("GetStudentRank/{id}")]
public async Task<ActionResult> GetStudentRank(Guid id){
...
}
var response = await HttpClient.GetAsync($"{BaseUrl}/Student/GetStudentRank/{id}");
I think, cause it is a Guid and not a string the type must be declared in the route and or the route must not start with a slash if it should be combined with the route on the class:
[HttpGet("{id:guid}/studentrank")]
public async Task<ActionResult> GetStudentRank(Guid id){
...
}

Model Binding HTTP Requests in ASP.Net Core WebAPI

I'm having an issue with an endpoint, it takes an object which isn't binding and consequently returning a 400 bad request.
I've gotten around this issue by passing in the individual properties of the object rather than the object itself, but would prefer the passing of an object.
WebClient webClient = new WebClient();
webClient.QueryString.Add("firstName", "value1");
webClient.QueryString.Add("lastName", "value2");
string result = webClient.DownloadString(url);
[HttpGet]
public async Task<IActionResult> DoSomething(string firstName, string lastName)
{
// this endpoint works perfectly
return Ok();
}
[HttpGet]
public async Task<IActionResult> DoSomething([FromBody]SubmitModel model)
{
// this endpoint returns a 400 bad request
return Ok();
}
public class SubmitModel
{
public string FirstName {get; set;}
public string LastName {get; set;
}
By design, GET request does not contain data in the request body.
So when your Submit method recieves a request, it can't bind the model from the body as the data does not exist and therefore returns an bad request response.
As your method is named Submit, it sounds like you should use a POST request instead. POST request, by design, sends data in the request body and is suited for submitting data to the server.
Try it like this
[HttpPost]
public async Task<IActionResult> Submit([FromBody]SubmitModel model)
{
// this endpoint returns a 400 bad request
return Ok();
}
I don't know if there is a good way to do what you're wanting. To get a little bit closer you can add this attribute to pull directly from the url
[HttpGet]
public ActionResult Get([FromUri]SubmitModel model)
{
// this endpoint returns a 400 bad request
return Ok();
}
Another thing you can do if need be is create an extension method that reflects over the model and adds all the properties/values to the query string. Some good examples here How do I serialize an object into query-string format?

Web API method return JSON data

I am using ASP.net web API 2.0 and would like my method to return the data in JSON format only.
Please suggest the code changes for this below method from the API controller class.
public async Task<List<Partner>> GetPartnerList()
{
return await _context.Partners.Take(100).ToListAsync();
}
You can use the Json<T>(T content) method of the ApiController
public async Task<IHttpActionResult> GetPartnerList() {
List<Partner> data = await _context.Partners.Take(100).ToListAsync();
return Json(data);
}
refactor action to return IHttpActionResult abstraction, await the data and pass it to the Json method which returns a JsonResult.
This means that regardless of content negotiation, the above action will only return JSON data.

HttpClient call to web api method with integer parameter Returns error 404

I really can't see what I am doing wrong here. I am trying to call a asp.net core web api method that accepts an integer via HttpClient but it returns a 404 error.
HospitalController (Web API)
[HttpGet("{id}")]
[Route("GetById")]
public JsonResult Get(int id)
{
return Json(_hospitalService.Get(id));
}
HospitalController (MVC)
protected string url = "http://localhost:5001/api/Hospital";
[HttpGet]
public async Task<IActionResult> Edit(int id)
{
if (id.Equals(0))
return StatusCode(404);
var accessToken = await HttpContext.GetTokenAsync("access_token");
client.SetBearerToken(accessToken);
HttpResponseMessage responseMessage = await client.GetAsync(url + "/GetById/" + id); //returns 404 error here.
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result;
var hospital = JsonConvert.DeserializeObject<Hospital>(responseData);
var hospitalVM = Mapper.Map<HospitalViewModel>(hospital);
return View(hospitalVM);
}
return View("Error");
}
I have a POST method in the same controller in MVC that works. But this GET method returns a 404 and I can't seem to know why.
There are two route templates being used in the api according to
[HttpGet("{id}")] //Matches GET api/Hospital/5
[Route("GetById")] //Matches GET api/Hospital/GetById
neither of which match what is being called
http://localhost:5001/api/Hospital/GetById/5
Http{Verb} attribute is usually used on RestFul APIs.
When building a REST API, it's rare that you will want to use [Route(...)] on an action method. It's better to use the more specific Http*Verb*Attributes to be precise about what your API supports. Clients of REST APIs are expected to know what paths and HTTP verbs map to specific logical operations.
Reference Routing to Controller Actions
update the route template on the web api to map to the desired route
[Route("api/[controller]")]
public class HospitalController : Controller {
//...code removed for brevity
//Matches GET api/Hospital/GetById/5
[HttpGet("GetById/{id:int}")]
public IActionResult Get(int id) {
return Ok(_hospitalService.Get(id));
}
}
Also HttpClient is meant to be used asynchronously, so the MVC controller would also need to be refactored as mixing blocking calls .Result can cause deadlocks
protected string url = "http://localhost:5001/api/Hospital";
[HttpGet]
public async Task<IActionResult> Edit(int id) {
if (id.Equals(0))
return StatusCode(404);
var accessToken = await HttpContext.GetTokenAsync("access_token");
client.SetBearerToken(accessToken);
HttpResponseMessage responseMessage = await client.GetAsync(url + "/GetById/" + id);
if (responseMessage.IsSuccessStatusCode) {
var responseData = await responseMessage.Content.ReadAsStringAsync();
var hospital = JsonConvert.DeserializeObject<Hospital>(responseData);
var hospitalVM = Mapper.Map<HospitalViewModel>(hospital);
return View(hospitalVM);
}
return View("Error");
}

Web API 2 - Method now allowed(405) for PUT

I am stuck with Web API 2 controller, from which I call PUT method and it gives me an error that method isn't allowed. I added lines of code in Web.config that prevent WebDAV to block methods. I tried everything but it is not working. It is probably problem with my PUT method in a controller.
Here is my controller code:
public IHttpActionResult Put(int id, [FromBody]ArticleModel model) {
var article = _articleService.UpdateArticle(model);
return Ok<ArticleModel>(article);
}
This is a code from where I call put :
response = await client.PutAsJsonAsync("api/article/2", articleModel);
before this code I defined client as http and added needed properties, and called other controller methods (GET, POST, DELETE) , they all work. This is from Windows Form app, and I am also calling from Postman but still the same error.
Add [HttpPut] , [RoutePrefix("api/yourcontroller")] and [Route("put")] attribute to your controller method
Example:
[RoutePrefix("api/yourcontroller")]
public class YourController
{
[HttpPut]
[Route("{id}/put")]
public IHttpActionResult Put(int id, [FromBody]ArticleModel model) {
var article = _articleService.UpdateArticle(model);
return Ok<ArticleModel>(article);
}
}
EDIT 1
public class YourController
{
[HttpPut]
[Route("api/article/{id}/put")]
public async Task<HttpResponseMessage> Put(int id, [FromBody]ArticleModel model) {
var article = _articleService.UpdateArticle(model);
return Ok<ArticleModel>(article);
}
}
From your HttpRequest call It seems what is expected is a HttpResponseMessage So changed the return type to async Task<HttpResponseMessage>
Code for making HttpRequest:
response = await client.PutAsJsonAsync("api/article/2/put", articleModel);
Add the [System.Web.Http.HttpPut] attribute to your method.

Categories

Resources