WebAPI not accepting Post Requests Successfully - c#

My controller for one of my WebAPIs was working perfectly yesterday, but today I made some changes to projects outside of the actual controller code and now the API is not posting correctly. This is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Markup;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace SanTool_WebAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class GeneratorStatusController : ControllerBase
{
static List<string> strings = new List<string>()
{
"value0", "value1", "value2"
};
[HttpGet]
public List<string> GetValues()
{
return strings;
}
[HttpPost("{input}")]
public List<string> Post(string input)
{
strings.Add(input);
return strings;
}
}
}
When I run the code using IIS explorer, and then navigate to https://localhost:44312/GeneratorStatus/, it displays my [HttpGet] correctly (displays the three strings), but when I try to use the post request with, https://localhost:44312/GeneratorStatus/2, it gives me error 405 and doesn't return the string

If you are just changing the URL in chrome this would be the problem. 405 often will mean that you are using the wrong http request type. When just changing the URL browsers issue a GET request to that resource.
You may want to test that same POST method with Postman.
Also see: Asp.Net Core 3.1 405 Method Not Allowed

First thing first.
I could be wrong here, but https://localhost:44312/GeneratorStatus/2, is something I would only use when I am getting things. In my experience, I have never seen a POST URL that looks like that.
Second,
I think, you are doing the Post wrong. First Up, I hope you are using Postman or curl to test your endpoints.
your POST would be something like this.
URL - If I am POSTing, the URL would be
https://localhost:44312/GeneratorStatus
with the Post Body, in your case, is a simple string, would look something like this.
{
input : "some input value"
}
Your Controller should probably look like this.
[HttpPost]
public List<string> Post(string input)
{
strings.Add(input);
return strings;
}

Related

C#/.NET POST request keeps returning 400 Bad Request in controller

I am trying to write a simple API te receive POST requests with a body. When I try to test my method it keeps resulting in a 400 bad request in Postman. I first thought the issue was with deserializing the JSON body. So to be sure I stripped out that logic from the controller, but the request still returned a 400 status code.
So I removed everything from my method except for the method itself, only returning Ok('Hello World'); and still the response was a 400.
What I have left for now is this:
[Route("api/v1/service/")]
public class ServiceController : Controller
{
public ServiceController()
{
}
[HttpGet("get")]
public IActionResult Get()
{
return Ok("GET works fine");
}
[HttpPost("post")]
public IActionResult Post()
{
return Ok("Hello World"); // <-- Keeps returning 400
}
}
The GET method works fine, but when I sent an empty POST call to /api/v1/service/post in Postman I get a bad request.
I also noticed that when I change the route to something different or random that does not exists it also gets a 400, instead of a 404.
So making a POST call to api/v1/service/this-route-is-not-defined also results in a bad request.
I keep changing small things in my request form adding/removing ContentType or Accept headers and adjusting my StartUp.cs . But every POST call I make to .NET seems to result in a 400 status code.
Edit
This might be related to the routing in Startup.cs:
app.UseHsts();
app.UseMvc(routes =>
{
});
app.UseRouting();
This is the request in POST man:
GET:
POST:
The code in the sample was offcourse altered from my original API method, but the idea is the same. I copied the sample to a new file in my project and clicked in Postman on create new request. So headers are the default ones.
Your missing MapControllers()
In your startup.cs add MapControllers(), this is required for attribute based routing.
app.MapControllers();
If the version of .NET you are using is < 6.0 then add like so:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
MapControllers is called to map attribute routed controllers.
First of all, the answers and comments given to this question were all helpfull.
I found the culprit. Apperently there was an option enabled in the Startup.cs file that puts an anti-forgery token check on all API calls that can modify stuff, like POST, PUT, DELETE. This is not an issue when calling the API from the frontend with a Javascript fetch() for instance. The token is added to a tag in the document and you can add to the request headers like this:
headers.append('X-XSRF-TOKEN', (document.getElementsByName("__RequestVerificationToken")[0] as any).value)
To be able to make a POST call from Postman for instance you can add this line temporarely above your action.
[IgnoreAntiforgeryToken]
So working example would like this:
[Route("api/v1/service/")]
public class ServiceController : Controller
{
public ServiceController()
{
}
[HttpGet("get")]
public IActionResult Get()
{
return Ok("GET works fine");
}
[IgnoreAntiforgeryToken]
[HttpPost("post")]
public IActionResult Post()
{
return Ok("Hello World"); // <-- Keeps returning 400
}
}
It is important to think about when to use [IgnoreAntiforgeryToken] and not to use it. On methods that allready expect an API key for instance you can use it in a production environment. But when method is public the anti-forgery token is a way of protecting your method from attackers or people/robots trying to spam your API.

Can't get response from ASP.NET web API

I'm setting up an ASP.net web API, but i keep getting 404 from all Http routes.
This is running on http://localhost:52011/ since that is the default location that VS2013 uses. I've added a simple test HttpRoute that just returns a string if it's called with a GET to test connectivity.
Directory browsing is enabled for feedback that the web API is in fact on localhost, and all directories of the project are visible.
I've tried setting the controller name fixed in the routes in WebApiConfig.cs and read a lot about the HttpRoute mappings, but i keep getting either a 404 or a "No Type was found that matches the controller named controllername".
I'm using Postman to test since that was recommended and easy to use.
Any tips are welcome!
WebApiConfig.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace BadgePrinter
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
//route to print badge.
config.Routes.MapHttpRoute(
name: "apiPrint",
routeTemplate: "api/{controller}/{action}/{Bedrijf}/{Aanspreektitel}/{Voornaam}/{Achternaam}/{Jobtitle}/{Kopies}/{Rijksregisternummer}/{BadgeAfdruk}/{printer}/{Image}",
defaults: new { Image = RouteParameter.Optional}
);
//test route to get string back
config.Routes.MapHttpRoute(
name: "apiTest",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
BadgeController.cs(the only controller in the project)
The first method is the one i'm going to have to use eventually, but the testmethod is the one i use for testing.
using BadgePrinter.CardTemplates;
using BadgePrinter.Interfaces;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
namespace BadgePrinter.Controllers
{
public class BadgeController : ApiController
{
//removed the first method since it was big and distracting.
// test method
[HttpGet]
public IHttpActionResult GetAllProducts()
{
return Ok("some product");
}
}
}
link for the testmethod that i'm using:
http://localhost:52011/api/BadgeController/Products/1
I also tried this link, even though that shouldn't matter:
http://localhost:52011/api/BadgeController/Products/1
Result:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:52011/api/BadgeController/Products/1'.",
"MessageDetail": "No type was found that matches the controller named 'BadgeController.cs'."
}
For the other HttpRoute, i keep getting 404, but i'll be in heaven if i can get the test to work :)
PS: i'm new to this kind of thing(ASP.net and API's) so i'm sorry if i'm being an idiot.
You have 2 issues here:
1) Your controller name BadgeController but in your API it should be Badge only. The correct URL should be:
http://localhost:52011/api/Badge/xxx
2) I can't see method name Products or something like that in your code.
3) If it is a POST method, it must have [FromBody] in parameter input.
There are some problems with your URL:
http://localhost:52011/api/BadgeController/Products/1
As far as I can see you don't have any GET method which would map to an ID parameter. All you have in your controller is
[HttpGet]
public IHttpActionResult GetAllProducts()
which doesn't accept any inputs.
2) The documented Web API convention is that you don't write "Controller" explicitly in the URL. Just the first part which is unique to the controller (Badge) in this case is required.
3) I can't see where you are getting the "Products" part of the URL from. There's no custom route definition or attribute which would make that part of the route. Again the convention is that you define an action method whose name starts with "Get" and that will map to a GET request to the controller name, with no further bits and pieces on the end. The rest of the method's name is irrelevant - and in your case AllProducts doesn't match to /Products anyway...so even if that was necessary it would still be incorrect.
In conclusion, the correct URL to access your GET action should be:
http://localhost:52011/api/Badge/
N.B. If you were to either change your existing GetAllProducts method, or add a second method like this:
[HttpGet]
public IHttpActionResult Get(int id)
{
return Ok("Product" + id);
}
Then you could use the ID in your URL, e.g.
http://localhost:52011/api/Badge/1
and it should route to this method.
Try dropping the controller part of the url:
http://localhost:52011/api/Badge/Products/1

C# Response.Write Error from scaffolded "dotnet new mvc"

I'm familiar with PHP and JS, as well as MVC methodology, but I'm completely new to C# and have spent time looking for the documentation on this specific error.
I used dotnet new mvc to create a working app on port 5000. Also note, I am working in the Controller, not the model or view:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using ExelonPrime.Models;
namespace OptimusPrime.Controllers{
public class ApiController : Controller
{
public void Help_Pdf()
{
Response.Write("test");
}
}
}
And the error I get (when trying to compile) is:
error CS1061: 'HttpResponse' does not contain a definition for 'Write' and no accessible extension method 'Write' accepting a first argument of type 'HttpResponse' could be found (are you missing a using directive or an assembly reference?)
If I'm missing a using directive, which one is it? I tried System.Web and that didn't work. How do I make this work?
I would recommend following through microsofts tutorial on using asp.net core.
https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/?view=aspnetcore-2.2
As far as this specific instance, rather than using Response.Write, I would do this
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using SampleWeb.Models;
namespace SampleWeb.Controllers
{
public class ApiController : Controller
{
[HttpGet]
public ActionResult<string> Help_Pdf()
{
return "test";
}
}
}
This specific sample might be helpful.
With this class, the url https://localhost:5001/api/Help_Pdf returns "test"
In ASPNET Core, when writing an API, it's more common to return an object, or POCO in stead of directly writing to the response stream (although it indeed is possible)
try changing your
public void Help_Pdf()
{
Response.Write("test");
}
to
[HttpGet()]
public IActionResult Help_Pdf()
{
return Ok();
}
this will return an 204 (no content), you can pass data however, to the OK function, to add a body to the response object.
If your trying to write directly to the response stream like that, you can do this:
public async Task Help_Pdf()
{
await Response.WriteAsync("Test");
}
However, I'd recommend not doing this in your actual app, the method should return an IActionResult. IMO it's not good practice to write directly to the stream in your controller methods (not very test friendly).

Upgraded from Umbraco v4 to v7 SurfaceController No route in the route table matches the supplied values

I recently converted our intranet Umbraco site from v4 to v7.2 and also converted all the webform masterpages to mvc.
I am trying to convert a usercontrol that should be a child action to a SurfaceController but I am getting the dreaded "No route in the route table matches the supplied values" error when trying to call the action:
#Html.Action("ServiceStatusInfo", "ServiceStatusSurface")
This is just a get action that doesn't require a view or a model. It just calls the action on the server and the server updates a file on the server that then get's read by some javascript.
I have done a lot of searching and I created a sample solution using Umbraco 7 and created a controllers folder, then a "MySurfaceController" and I was able to call the action from the masterpage of the sample solution with no issues but in the recently converted project it seems like there is some weird routing issue going on. I compared the web.config's for both the current project and the sample one and they pretty much have the same entries (I thought maybe I missed something). It seems that my converted project is not recognizing the routing. Any help will be appreciated.
Here is the SurfaceController
using Umbraco.Web.Mvc;
using System.Web.Mvc;
namespace MyUmbracoApp.Controllers
{
public class ServiceStatusSurfaceController : SurfaceController
{
// can't reach this either:
public ActionResult Index()
{
return Content("hello world");
}
// this is what I am trying to reach
[ChildActionOnly]
public ActionResult ServiceStatusInfo()
{
// do some stuff to get the status
return CurrentUmbracoPage();
}
}
}
I have also tried using the "PluginController" option even though this is not a plugin with the "area" attribute but same problem.
Maybe there is a workaround that I am not aware of ?
Change StatusInfo to ServiceStatusInfo in your action call. This should match the name of the action.
#Html.Action("ServiceStatusInfo", "ServiceStatusSurface")

Where is Request.CreateErrorResponse?

I saw it in this blog post, but that doesn't actually say how to "enable" it. And it seems that by default it isn't enabled.
I know it's an extension method, as defined here: http://msdn.microsoft.com/en-us/library/hh835786(v=vs.108).aspx but how do I get access to it? If I type Request.CreateErrorResponse then the compiler doesn't recognize it.
I'm already using System.Net.Http.
I've noticed this in release version as well. And if you don't have the right using statement, it'll error. You need :
using System.Net.Http;
even if you already have this:
using System.Web.Http.Controllers;
Are you still using pre-release or release version? There were a number of extensions that did not appear until just before the release and did not exist in earlier versions of the webapi release. I am unsure if this was one of them, but it may be what is causing your problem.
Request is the public property of the ApiController class and should be available to you in any API actions in that controller.
public abstract class ApiController
{
...
public HttpRequestMessage Request { get; set; }
}
Here is a small code sample that works for me:
using System.Net;
using System.Net.Http;
using System.Web.Http;
public class MyFirstApiController : ApiController
{
// GET
public HttpResponseMessage Get(int id)
{
return Request.CreateErrorResponse(HttpStatusCode.ExpectationFailed, "Some message here");
}
}
Interestingly enough, if I take away using System.Net.Http; statement, Request no longer has a CreateErrorResponse() method.
Although important points have been answered.
One more dumbest possibility is that you might have chosen wrong type of controller.
Make sure you have created an Web API controller, incase of MVC controller you cannot have
Request.CreateErrorResponse()
Just another possibility where you may find this issue.
In .net core, add Microsoft.AspNetCore.Mvc.WebApiCompatShim.dll .
https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httprequestmessageextensions.createerrorresponse?view=aspnetcore-2.0
Use System.Web.Http assembly.
source: CreateErrorResponse Method
you can use from that In block Try-Catch.
[HttpGet]
public HttpResponseMessage DownloadVideo(Guid id, string title)
{
try
{
//Your Code at the end return result
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex.Message);
}
}
Inject and instance of the HttpRequestMessage in your controller constructor or method:
Task<ActionResult<GetOneShopDtoResponse>> GetOne(HttpRequestMessage request,int shopId) {
return request.CreateErrorResponse(.....)
}
My best guess is that it is the third assembly, that you haven't referenced, that is the problem.
Unfortunately I don't have access to my computer, browsing on my phone, so I can't track down the file for you. According to the docs it should be in System.Web.Http.SelfHost.dll, so I guess you could just search for that file to locate it.

Categories

Resources