I got the following address:
api/users/AddNew/<object here>
And so, I did something like this to test it out:
api/users/AddNew/{"Id":0,"NameFirst":"NewUser","NameLast":"NewUserLast","DateOfBirth":"2018/07/27"}
But all I get is a "This localhost page can't be found". Debugging in Visual Studio I set a breakpoint right when the code is supposed to trigger, but it never hits the debug point. Code below:
[HttpPost(Name = "AddNew")]
[Route("AddNew/{jsonUser}")]
public ActionResult<User> Post([FromBody] User newUser)
{
if (newUser != null) // Debug Point here never triggers
{
return Facade.AddNewUser(newUser);
}
else
{
return new User() { Id = 0, NameFirst = ErrorCodeUtility.GetEnumName(ErrorCodes.API_INVALID_POST_OBJECT) };
}
}
The User Model:
public class User
{
public uint Id;
public String NameFirst;
public String NameLast;
public DateTime DateOfBirth;
}
So, I'm assuming I'm doing something wrong either with my request or my C# code, but I can't quite find out which one of them it is. Sorry I'm a bit new at this :)
There are at least three problems here:
You cannot call a POST method by navigating to an URL, since that executes a GET. You need a tool such as Postman for this testing.
[FromBody] means that the data will come as part of the body of the request, so the data would be ignored even if the request could be processed (which cannot be, as explained in point 1). Note, however, that since you are using ASP.NET Core 2.1, you don't actually need to use [FromBody] as that is the default, as explained here.
You say you want to create a REST API. /AddNew doesn't follow REST.
Your method should be:
[HttpPost]
public ActionResult<User> Post([FromBody] User newUser)
And your URL would then be:
localhost:somePort/api/users
Related
I have multiple actions methods in a Controller that share the same ActionName, like this:
[ActionName("Example")]
[DefinedParameter(Name = "xID")]
[HttpPost()]
public ActionResult ExampleX(Guid xID)
{
....
}
[ActionName("Example")]
[DefinedParameter(Name = "yID")]
[HttpPost()]
public ActionResult ExampleY(Guid yID)
{
....
}
In addition to this I use a action selector via the DefinedParameter attribute, like this:
public class DefinedParameterAttribute : ActionMethodSelectorAttribute
{
public string Name { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
HttpRequestBase request = controllerContext.RequestContext.HttpContext.Request;
return request[this.Name] != null;
}
}
In most cases this works perfectly.
In some cases however I get the following error:
"AmbiguousMatchException
Exception message: The current request for action 'Example' on controller type 'ExampleController' is ambiguous between the following action methods: ...."
I get this error when:
I submit a request (that takes some time) with example parameter xID.
While the request from step 1 is still processing on the server (no response returned yet), I submit a second request (example parameter yID).
I discovered that in those cases the request indeed contains both parameters; in my example xID and yID. That explains the error.
So it seems that the server somehow reuses the same request and adds the parameter from the second submit.
NB: Please know that I mostly use action methods with a unique name. That's the best way to go. But in some cases the described example can be a better approach.
How can I solve and/or avoid this error?
MVC doesn't support method overloading based solely on signature, so this will fail:
Same question posted here
Check this blog for same example
Please see the code below, which is similar to some code I am looking at:
[HttpGet]
public IActionResult Create([FromHeader(Name = "x-requestid")] string requestId)
{
return View();
}
[HttpPost]
public IActionResult Create(Person person, [FromHeader(Name = "x-requestid")] string requestId)
{
if (ModelState.IsValid)
{
string test = "got here";
}
return View();
}
requestId is always null. How is requestId populated?
I have read lots of questions over the last two hours e.g. this one: What is the X-REQUEST-ID http header?. An answerer to another question suggesting installing the following Nuget package:
Microsoft.ApplicationInsights.ASPNetCore
However, this made no difference.
In general the "x-*" headers are non-standard ones.
That particular one is probably coincidentally used within Application Insights to uniquely identify a request, but it's unlikely to be sending requests to your server including it.
Whatever client is sending requests to your server has to be explicitly adding that header for you to receive it there; it's not part of any standard HTTP request.
You are bound to request header with name "x-requestid", but it should be called "X-Request-ID". Just try:
[HttpGet]
public IActionResult Get([FromHeader(Name = "x-request-id")] string requestId)
{
return View();
}
I was walking throug the angular heroes tutorial and wanted to replace the in-memory-service with a real backend. I wanted to create an ASP.net WebAPI. The controller worked completly fine like this with my angular frontend:
[EnableCors(origins: "http://localhost:4200", headers: "*", methods: "*")]
public class HeroesController : ApiController
{
private static readonly List<Hero> heroes;
static HeroesController()
{
heroes = new List<Hero>()
{
new Hero { id = 11, name = "Mr. Nice" },
new Hero { id = 12, name = "Narco" }
};
}
public IEnumerable<Hero> Get()
{
return heroes;
}
public Hero Get(int id)
{
return heroes.Find(x => x.id == id);
}
public Hero Post([FromBody] Hero hero)
{
hero.id = heroes.Max(x => x.id) + 1;
heroes.Add(hero);
return hero;
}
public void Put([FromBody] Hero hero)
{
Hero oldHero = heroes.FirstOrDefault(x => x.id == hero.id);
if (oldHero != null)
{
heroes.Remove(oldHero);
heroes.Add(hero);
}
}
public void Delete(int id)
{
Hero oldHero = heroes.FirstOrDefault(x => x.id == id);
if (oldHero != null)
{
heroes.Remove(oldHero);
}
}
}
Which was nice and cool until I tried to create a method which was not named like "get", "post", "put" or "delete". I thought I could insert another public method like this:
public void DoSomething()
{
}
From now on every method continued to work EXCEPT the post. When I wanted to trigger the post I got the following Messages:
OPTIONS http://localhost:52835/api/heroes 500 (Internal Server Error)
Failed to load http://localhost:52835/api/heroes: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
ERROR TypeError: Cannot read property 'id' of undefined
ERROR CONTEXT DebugContext_ {view: {…}, nodeIndex: 2, nodeDef: {…}, elDef: {…}, elView: {…}}
Yeah, the angular frontend runs on port 4200 and the webapi on 52835. Therefor I have installed and activated CORS which workes very well until I added the mentioned empty method. If I change the method to private, everything works fine again. I don't get it. Please help me :(
Simply adding the Http method like
[HttpPost]
public void DoSomething()
didn't work for me. The method was HttpPost by default (like you can read in the first link further down). And as I changed to HttpGet my get-Methods stopped working. It seams there's some kind of black magic in the background which trys to figure out which methods could fit to a type of http requests depending on the name and the signature of a method. And if I understood right, complex types (like my hero) won't be checked, so basically the signatures of my methods "Post" and "DoSomething" are equal. Therefor read the helping link of ADyson:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/routing-and-action-selection#action-selection
If there's more than one method fitting, the black magic starts jumping out the window. With this said, what will help is changing the method's signature (of course). For example by adding a parameter of type int.
But I guess the better solution would be defining a route with the Route Annotation like this:
[Route("heroes/doSomething")]
public void DoSomething()
Again thanks to ADyson (you are my hero ;) ):
https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
Edit:
Of course it's not black magic. As I read the linked pages, everything became clear. The logic is, methods which contain Post in theire names will serve post requests. And if the automatism can not derive a request type from the method name, then the method is automatically post. And if now two methods serve post requests, the automatism gets confused if no clear routes are given.
I had a similar issue and what solved it for me was to decorate the method with the relevant decorator.
e.g.
[HttpPost]
public void DoSomething()
{
}
I have simple console application, which send json data to controller.
Client.PostAsJsonAsync("http://[site].azurewebsites.net/api/Decision", report);
And controller:
public class DecisionController : ApiController
{
private readonly TaskManager _taskManager = new TaskManager();
[HttpGet]
public Decision Get(int id)
{
return _taskManager.GetDecision(id);
}
[HttpPost]
public void Post(Decision decision)
{
_taskManager.UpdateDecision(decision);
}
}
Visual Studio debugger shows, that a request does not reach the controller (not trigger a breakpoint). Console application does not throw any exceptions, and report variable does not equal to null.
How can I debug this?
You might want to take a look at Fiddler which is a free tool to debug web requests. Basically, it monitors all web requests going through your machine and lets you see what they look like. You can also generate new ones straight from there too. That way, you can at least make sure that your request is properly formatted.
Also, I noticed that you are trying to call your controller directly when you should be calling an action on your controller instead. When you request GET api/Decision, you also need to provide an action method. I do not know enough of your model to restructure your controller properly, but here is an example based on a simple user profile.
public class ProfileController : ApiController
{
[HttpGet]
public string FullName()
{
return "Foo Bar";
}
[HttpPost]
public void FullName(string newName)
{
// ...
}
[HttpGet]
public int Age()
{
return 22;
}
[HttpPost]
public void Age(int newAge)
{
// ...
}
}
As you can see, the HttpVERB attribute is not used to dispatch requests to a controller to specific methods. It is used to distinguish the different overloads of related restful actions. In this case, it is used to distinguish between getting and setting a FullName and an Age property.
If you with to keep your controller intact, you should be requesting:
POST http://[site].azurewebsites.net/api/Decision/post
GET http://[site].azurewebsites.net/api/Decision/get
I use Web API 2 Attribute Routing in my project to provide JSON interface over my data. I am facing weird behaviour of controller selection, not decided yet whether it's a bug or a feature :)
Let me describe my approach.
I would like to simulate OData syntax with help of attribute routing (direct OData usage has been refused due to design principles). For example, to get entity with id=5 I use HTTP GET request to URI http://mydomain.com/api/Entity(5) . I expect to use the same URI with HTTP PUT verb to update the entity. This is where the journey begins...
I would like to have separate controller for getting entities (FirstController in the example provided below) and another one for modifying entities (SecondController). Both controllers handles the same URI (e.g. http://mydomain.com/api/Entity(5)) the only difference is HTTP verb used with the URI - GET should be handled by FirstController, PUT should be handled by SecondController. But the URI is handled by none of them; instead HTTP 404 error is returned.
When I "merge" GET and PUT actions to only one controller (commented out in FirstController), both verbs are handled correctly.
I am using IIS Express and all conventional routes are disabled, only attribute routing is in charge.
It looks like the controller selection process does not work with HTTP verb. In another words, HttpGet and HttpPut attributes just limit action usage but they do not serve as criteria during controller selection. I am not so familiar with MVC / Web API fundamentals, so let me ask you my big question:
Is the behaviour, described herein before, a feature intentionally implemented by MVC / Web API 2 or a bug to be fixed?
If it is considered as a feature, it prevents me to follow design principles. I can live with "merged" controllers but still considering it as a bad practice...
Or am I missing something in my train of thought?
My environment setup:
Windows 7 (virtual machine using Oracle VirtualBox)
Visual Studio 2013
.NET 4.5.1
Web API 2
The following is implementation of FirstController class:
public class FirstController : ApiController
{
[HttpGet]
[Route("api/Entity({id:int})")]
public Output GetEntity(int id)
{
Output output = new Output() { Id = id, Name = "foo" };
return output;
}
//[HttpPut]
//[Route("api/Entity({id:int})")]
//public Output UpdateEntity(int id, UpdateEntity command)
//{
// Output output = new Output() { Id = id, Name = command.Name };
// return output;
//}
}
The following is implementation of SecondController class:
public class SecondController : ApiController
{
[HttpPut]
[Route("api/Entity({id:int})")]
public Output UpdateEntity(int id, UpdateEntity command)
{
Output output = new Output() { Id = id, Name = command.Name };
return output;
}
}
The following is implementation of a console application to test the described behaviour:
class Program
{
static void Main(string[] args)
{
// HTTP client initialization
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:1567");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET - FirstController.GetEntity
HttpResponseMessage getEntityResponse = httpClient.GetAsync("/api/Entity(5)").Result;
Output getOutput = getEntityResponse.Content.ReadAsAsync<Output>().Result;
// HTTP PUT - SecondController.UpdateEntity
UpdateEntity updateCommand = new UpdateEntity() { Name = "newEntityname" };
HttpResponseMessage updateEntityResponse = httpClient.PutAsJsonAsync("/api/Entity(10)", updateCommand).Result;
Output updateOutput = updateEntityResponse.Content.ReadAsAsync<Output>().Result;
}
}
For completion, the following are used DTOs:
public class UpdateEntity
{
public string Name { get; set; }
}
public class Output
{
public int Id { get; set; }
public string Name { get; set; }
}
Thanks in advance for your responses,
Jan Kacina
This design was intentional as we thought it to be an error case where a user would be having same route template on different controllers which can cause ambiguity in the selection process.
Also if we keep aside attribute routing, how would this work with regular routing? Let's imagine we have 2 regular routes where first one is targeted for FirstController and the second to SecondController. Now if a request url is like api/Entity(5), then Web API would always match the 1st route in the route table which would always hit the FirstController and would never reach SecondController. Remember that once Web API matches a route it tries to go till the action selection process and if the action selection process doesn't result in an action being selected, then an error response is sent to the client. You probably are assuming that if an action is not selected in one controller then Web API would route it to the next one in the route configuration. This is incorrect.
Route probing occurs only once and if it results in a match, then the next steps take place...that is controller and action selection. Hope this helps.