I am running into a problem with a .NET Core tutorial. It is written in .NET Core 2.2, but I want to use the current release 3.0.
This is also the only difference I can find in my setup vs the tutorial's.
The issue is as follows:
I have a HttpPost route with a CreatedAtRoute call in it, but that can't find the route it has to.
I always get this error when testing through Postman:
System.InvalidOperationException: No route matches the supplied values.
at Microsoft.AspNetCore.Mvc.CreatedAtRouteResult.OnFormatting(ActionContext context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsyncCore(ActionContext context, ObjectResult result, Type objectType, Object value)
...
But when checking through the debugger I see that everything goes fine, excpet for this line. So the new photo I want to upload is also added in Cloudinary and into the database.
The call I make is:
return CreatedAtRoute("GetPhoto", new {id = photo.Id}, photoToReturn);
This should find this route, in the same file:
[HttpGet("{id}", Name = "GetPhoto")]
public async Task<IActionResult> GetPhoto(int id)
{
Is there anything I miss here? I haven't found any useful answer yet...
Edit: public repo available, when cloning and running this, I still get the same error...
Edit 2: in the meanwhile, the tutorial covered more topics and I have found this:
CreatedAtRoute that calls another Controller with:
return CreatedAtRoute("GetUser",new {Controller="Users",id=createdUser.Id}, userToReturn);
works, when trying to call a route inside the same controller, it fails, also with this one:
return CreatedAtRoute(nameof(GetMessage), new {Controller = "Messages", id = message.Id}, message);
To call this route in the same controller:
[HttpGet("{id}", Name = "GetMessage")]
public async Task<IActionResult> GetMessage(int userId, int id)
Instead of
[HttpGet("{id}", Name = "GetPhoto")]
Use:
[HttpGet("/{id}", Name = "GetPhoto")]
I was getting same error and i solved it by putting '/' before id.
Related
I have this simple API controller in a NetCore 2.2 web game that is supposed to return a list of monsters based on the dungeonID(Guid).
So I use the URL, passing in the guid of the dungeonID, to that controller like this:
https://localhost:44361/MonsterList/GetMonsters/2f14c8gf-2e7e-466a-bcbg-f4440e92b3dg
But when I step through the code, I just see all zeroes for the dungeonID:
public async Task<JsonResult> GetMonsters(Guid dungeonID)
{
var monsters = await _context.MonsterList.Where(c => c.DungeonID == (dungeonID)).ToListAsync();
return Json(monsters);
}
This returns nothing because, for reasons I don't know, dungeonID is always all zeroes.
But this does work if I hard-code in the dungeonID:
https://localhost:44361/MonsterList/GetMonsters
public async Task<JsonResult> GetMonsters()
{
var monsters = await _context.MonsterList.Where(c => c.DungeonID == Guid.Parse("2f14c8gf-2e7e-466a-bcbg-f4440e92b3dg")).ToListAsync();
return Json(monsters);
}
I've seen lots of posts similiar to mine, like these:
asp.net webapi 2 post parameter is always null
Post parameter is always null
But nothing seems to work.
How do I add the ability to pass in a Guid parameter?
Thanks!
One point is 2f14c8gf-2e7e-466a-bcbg-f4440e92b3dg is not a GUID/UUID . Just try with a correct GUID :
https://localhost:44384/api/values/GetMonsters/2e6ae748-10c2-4e23-84c3-9d3db7c09631
I have below code implemented Web API (.net Framework 4.5.2). When I make a call "http://localhost:3000/123" - It fetches user details whose id is 123.
If I make "http://localhost:3000/Class1/?status=Active" - It fetches user details who belong to Class 1 and status as active. Same I converted to .net core and eventhough I mentioned FromQuery, call always goes to ":http://localhost:3000/123"
public class UserController : Controller
{
private Repository repository;
[HttpGet("{id}")]
public object Get(string id)
{
return repository.GetUser(id) ?? NotFound();
}
[HttpGet("{group}")]
public object Get(string group, Status status)
{
// Get the User list from the group and whose status is active
}
}
Please let me know how to resolve this without changing Route Parameter.
Simply, you have two conflicting routes here. There's no way for the framework to know which to route to, so it's just going to take the first one. As #Nkosi indicated, if there's some kind of constraint you can put on the param, that will help. You may not be able to restrict to just ints, but perhaps there's a particular regex, for example, that would only match one or the other. You can see your options for constraining route params in the relevant docs.
If there's no clear constraint you can apply that will not also match the other, then you're mostly out of luck here. You can simply change one of the routes to be more explicit, e.g. [HttpGet("group/{group}")]. If the route absolutely must be the same, your only other option is to have one action handle both cases, branching your code depending on some factor.
[HttpGet("{id}")]
public object Get(string id, Status? status = null)
{
if (status.HasValue)
{
// Note: treat `id` as `group` here.
// Get the User list from the group and whose status is active
}
else
{
return repository.GetUser(id) ?? NotFound();
}
}
That may not be the best approach (branching on presence of status), but it's just an example. You'd need to decide what would work best here.
I've got what I take to be a very standard controller:
Here's the controller definition (a bit of dependency injection, but standard):
public class SeriesController : Controller
{
private readonly IHostingEnvironment _env;
public SeriesController(IHostingEnvironment env)
{
_env = env;
}
[HttpGet("/series/{id:int?}/{title?}")]
public IActionResult Index(int id, string title)
{
if (id > 0)
{
var populateSeriesItem = new PopulateSeriesItem(_env, new SqlConnection());
var seriesItem = populateSeriesItem.GenerateSeriesItem(id);
//...
If the id is absent (or 0), then it displays all records; if it's 1 or greater, it displays a single record. (This is how the client wants it!)
I call it as follows:
https://localhost/series/3/title
or
https://localhost/series/4/
but the problem is id is always 0 (and title null).
That's the case if I type it into the URL bar or specify id manually (i.e., /series?id=3)
I just can't figure out what I'm missing.
The exact same setup works perfectly with a different controller.
[HttpGet("/books/{id:int?}/{title?}")]
public ActionResult Index(int id, string title)
{
if (id > 0)
{
var populateBookItem = new PopulateBookItem(new SqlConnection(), _env);
var bookItem = populateBookItem.GenerateBookItem(id);
That one works.
This is the routing configuration (just standard):
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
I must be missing something obvious, but I'm baffled!
Any advice greatly appreciated.
Update:
I've stripped away all the functionality from the controller. I'm just literally passing it to a view.
This works: https://localhost:44319/series/14/megacities - the view reports id = 14.
This works: https://localhost:44319/series/5/megacities - the view reports id = 5
This works: https://localhost:44319/series/14/business-with-china - the view reports id = 14.
This doesn't work: https://localhost:44319/series/5/business-with-china - it goes to https://localhost:44319/series/0.
There are other URLs that follow the same pattern. Some working, some not working.
If I strip out the title parameter, they all seem to work.
I've no idea why!
Remove routes.MapRoute, it isn't necessary for Web API.
HTTPGet should look like [HttpGet("/books/{id:int?}")]
Add query parameters to request GET api/books/5?title=abc
Method definition:
[HttpGet("/books/{id:int?}")]
public ActionResult Index(int id, [FromQuery] string title)
{
//code here
}
If there is a mismatch in names, parameter will resolve as default value - 0 in case of numbers and null otherwise
In the example below, url has param1 whereas the method has parameter name as param2.
[HttpGet("/foo/{param1}")]
public ActionResult Foo(int param2)
{
//code here
}
I had the same problem so I found this link. I accidentally found the solution for my problem so I even it is not related to your, I post it for anyone who comes here who may have the same problem as me.
I am using C# and .net Core 2.2
I was getting id value to be 0 no matter what I did.
It happened that the parameter name I chose, which was "page", the page number, needed to be "id" the same name as the specified in {id?}
Thanks for everybody's help.
I've solved the problem, though I don't know what was causing it. (I think it might have been a browser caching issue.)
Anyway, I restarted the server, and everything now works as it should.
I have a webforms project, and am attempting to run some code that allows me to make a call to an MVC route and then render the result within the body of the web forms page.
There are a couple of HttpResponse/Request/Context wrappers which I use to execute a call to an MVC route, e.g.:
private static string RenderInternal(string path)
{
var responseWriter = new StringWriter();
var mvcResponse = new MvcPlayerHttpResponseWrapper(responseWriter, PageRenderer.CurrentPageId);
var mvcRequest = new MvcPlayerHttpRequestWrapper(Request, path);
var mvcContext = new MvcPlayerHttpContextWrapper(Context, mvcResponse, mvcRequest);
lock (HttpContext.Current)
{
new MvcHttpHandlerWrapper().PublicProcessRequest(mvcContext);
}
...
The code works fine for executing simple MVC routes, for e.g. "/Home/Index". But I can't specify any query string parameters (e.g. "/Home/Index?foo=bar") as they simply get ignored. I have tried to set the QueryString directly within the RequestWrapper instance, like so:
public class MvcPlayerHttpRequestWrapper : HttpRequestWrapper
{
private readonly string _path;
private readonly NameValueCollection query = new NameValueCollection();
public MvcPlayerHttpRequestWrapper(HttpRequest httpRequest, string path)
: base(httpRequest)
{
var parts = path.Split('?');
if (parts.Length > 1)
{
query = ExtractQueryString(parts[1]);
}
_path = parts[0];
}
public override string Path
{
get
{
return _path;
}
}
public override NameValueCollection QueryString
{
get
{
return query;
}
}
...
When debugging I can see the correct values are in the "request.QueryString", but the values never get bound to the method parameter.
Does anyone know how QueryString values are used and bound from an http request to an MVC controller action?
It seems like the handling of the QueryString value is more complex than I anticipated. I have a limited knowledge of the internals of the MVC Request pipeline.
I have been trying to research the internals myself and will continue to do so. If I find anything I will update this post appropriately.
I have also created a very simple web forms project containing only the code needed to produce this problem and have shared it via dropbox: https://www.dropbox.com/s/vi6erzw24813zq1/StackMvcGetQuestion.zip
The project simply contains one Default.aspx page, a Controller, and the MvcWrapper class used to render out the result of an MVC path. If you look at the Default.aspx.cs you will see a route path containing a querystring parameter is passed in, but it never binds against the parameter on the action.
As a quick reference, here are some extracts from that web project.
The controller:
public class HomeController : Controller
{
public ActionResult Index(string foo)
{
return Content(string.Format("<p>foo = {0}</p>", foo));
}
}
The Default.aspx page:
protected void Page_Load(object sender, EventArgs e)
{
string path = "/Home/Index?foo=baz";
divMvcOutput.InnerHtml = MvcWrapper.MvcPlayerFunctions.Render(path);
}
I have been struggling with this for quite a while now, so would appreciate any advice in any form. :)
MVC framework will try to fill the values of the parameters of the action method from the query string (and other available data such as posted form fields, etc.), that part you got right. The part you missed is that it does so by matching the name of the parameter with the value names passed in. So if you have a method MyMethod in Controller MyController with the signature:
public ActionResult MyMethod(string Path)
{
//Some code goes here
}
The query string (or one of the other sources of variables) must contain a variable named "Path" for the framework to be able to detect it. The query string should be /MyController/MyMethod?Path=Baz
Ok. This was a long debugging session :) and this will be a long response, so bear with me :)
First how MVC works. When you call an action method with input parameters, the framework will call a class called "DefaultModelBinder" that will try and provide a value for each basic type (int, long, etc.) and instance of complex types (objects). This model binder will depend on something called the ValueProvider collection to look for variable names in query string, submitted forms, etc. One of the ValueProviders that interests us the most is the QueryStringValueProvider. As you can guess, it gets the variables defined in the query string. Deep inside the framework, this class calls HttpContext.Current to retrieve the values of the query string instead of relying on the ones being passed to it. In your setup this is causing it to see the original request with localhost:xxxx/Default.aspx as the underlying request causing it to see an empty query string. In fact inside the Action method (Bar in your case) you can get the value this.QueryString["variable"] and it will have the right value.
I modified the Player.cs file to use a web client to make a call to an MVC application running in a separate copy of VS and it worked perfectly. So I suggest you run your mvc application separately and call into it and it should work fine.
In my Asp.net MVC app, I have two methods on a controller, one for when the user first arrives on the view and then one when they submit the form on said view.
public ActionResult Foo() {}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Foo(string id, Account accountToFoo) {}
In the second action, there's a custom model binder that's assembling the account object that I'm acting on, though that's really not important. This all works fine in testing locally on a server.
We try to be pretty good about writing unit tests to test all our different views are properly getting routed to, including those that are HTTP POST. To do so, we've been using mvccontrib's test helper.
Testing gets have been super simple
"~/account/foo/myusername".
Route().
ShouldMapTo<AccountController>(c => c.Foo("myusername"));
My question is in testing POST routes, how do I write the lambda that I would use to verify the post is receiving accurate values, similar to the GET test above?
For a POST, it looks something like:
"~/account/foo".
WithMethod(HttpVerbs.Post).
ShouldMapTo<AccountController>(a => something_something);
It's the something_something portion of my lambda that I'm having trouble with. Using arbitrary values doesn't work ("a => a.Foo(0, new Account()"). How would I specify the expected values as part of the test?
EDIT I was hoping there was something akin to the way Moq has lambdas for statements such as foo.Setup(s => s.Foo(It.IsAny(), It.Is(i => i > 32)) and so on. Even I have to explicitly supply the values, that's workable--I just can't seem to grok the desired structure to pass those explicit values.
Here's an example. Assuming you have the following action:
public AccountController : Controller
{
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Foo(string id)
{
return View();
}
}
And the following route registered:
RouteTable.Routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "home", action = "index", id = "" }
);
You could test it like this:
var routeData = "~/account/foo".WithMethod(HttpVerbs.Post);
routeData.Values["id"] = "123";
routeData.ShouldMapTo<AccountController>(c => c.Foo("123"));
Some tweaking might be necessary to include the second Account argument you have.
Using the mvccontrib helper syntax:
"~/account/foo".WithMethod(HttpVerbs.Post).ShouldMapTo<AccountController>(a => a.foo(null));
You pass null as the Foo(string id, Account accountToFoo) method is never executed as part of the routing test.