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

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.

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){
...
}

Action method not showing in Web API, why?

I have Web API, I have written action method. but it's not correctly visible when I run the application. I cannot see SendPushNotification in the attached image.
Controller Code:
[RoutePrefix("api/OTP")]
public class OTPController : ApiController
{
public IHttpActionResult Get(int id)
{
return Ok("value");
}
[HttpGet]
public IHttpActionResult SendPushNotification(string userId, string factorId, string domain)
{
var response = _oTPRepository.SendPushNotification(userId, factorId, domain);
return Ok(response);
}
add a Route over your method, something like this:
[HttpGet]
[Route("SendPushNotification")]
public IHttpActionResult SendPushNotification(string userId, string factorId, string domain)
{
var response = _oTPRepository.SendPushNotification(userId, factorId, domain);
return Ok(response);
}
This will combine with the RoutePrefix from your controller and give you what you want.
You can call it whatever you want as well, whatever makes sense for your API.
in mentioned image second method is that for which you are actually looking for.
default routing for action methods is api/{controller_name}.
if you want to access that method as your given name you have set routing attribute above that action method.
like [Routing("api/OTP/SendPushNotification")]

Web API HttpPost method always receives GET

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

Is there any way to get full Url to other controller method from code?

I have Asp.Net Core web application. With following controller
[Produces("application/json")]
[Route("api/[controller]")]
public class TestController
{
[HttpGet]
[Route("firstroute")]
public async Task<IActionResult> FirstMethod()
{
...Some code...
}
[HttpGet]
[Route("secondroute")]
public async Task<IActionResult> SecondMethod()
{
...
SomeMethod(redirectLink)
...
}
}
What I need is to get fully assembled redirectLink to FirstMethod (it will probably be similar to this: "http://localhost/api/test/firstroute").
I need not RedirectToAction, but exact Url as string.
Didn't manage to find any suitable methods in this.Url or Microsoft.AspNetCore.Http.Extensions.
this.Request.GetDisplayUrl() returns result in appropriate format, but only for the called method.
you can use data from HttpContext.Request like bellow
var Url = string.Format("{0}://{1}{2}", HttpContext.Request.Scheme,HttpContext.Request.Host,"/api/firstroute");
and rediret by
return Redirect(Url);

ASP.NET Core Middleware

I have build custom Identity Middle-ware. Inside Invoke method I am checking if request has token etc. After token is checked I want to pass request further to controller. It`s working for GET request - it jump into controller method. It is not working for POST request.
Here is Invoke Method
public async Task Invoke(HttpContext context)
{
//checking
await _next(context);
}
Its working controller:
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[AllowAnonymous]
[HttpGet]
public string Get()
{
return "Allow annymous";
}
}
And not working one
[Route("api/[controller]")]
public class AccountController : Controller
{
[AllowAnonymous]
[HttpPost]
public void Login()
{
//some logic
HttpContext.Response.WriteAsync("Unauthorized");
}
}
Making POST call Postman returns 404 not found.
The route for the action in question would be /api/Account. Make sure that is correct.
If instead you wanted /api/Account/Login:
[Route("api/[controller]/[action]")]
public class AccountController : Controller
{
[AllowAnonymous]
[HttpPost]
public void Login()
{
//some logic
HttpContext.Response.WriteAsync("Unauthorized");
}
}
Try returning something like an IActionResult rather than simply void.
[AllowAnonymous]
[HttpPost]
public IActionResult Login()
{
// some logic
return Unauthorized();
}
Looking at your code I hopping there is only one method inside the AccountController called Login. If not please add attribute [Route("Login")] to Login method (make sure you do not keep it empty like [Route("Login")] otherwise it will have same as what you are doing currently).
Then make the call to the POST http://host:***/api/account/login url and that should work.
FYI: I have tried this thing on my machine and it is working.

Categories

Resources