Asp.net core how to get Id from url - c#

Hay, I'm creating a website like wikipedia and I need to get the id from url to show correct view. This is my route config:
private void ConfigureRoutes(IRouteBuilder routeBuilder)
{
routeBuilder.MapRoute(
"Default",
"{controller=Home}/{action=Index}/{id?}"
);
}
The id will be name of the view that controller will return.

Where are you getting stuck? The default mapping that Visual Studio sets up for you supports this already. Just add an id parameter to your controller method and use it to look up your content.
public ActionResult Index(string id)
{
if (id != null)
{
// Lookup topic from id
}
}

Related

MVC controller method for View with optional Guid Parameter

I am trying to make a MVC view accessible from either directly going to that View from a menu or by clicking on a link that'll take me to that same view but with a parameter and with that particular links information instead of seeing a general page if I went straight to it.
public ActionResult Roles(Guid applicationId)
{
if (applicationId == Guid.Empty)
{
return View();
}
var application = new ApplicationStore().ReadForId(applicationId);
return View(application);
}
I know for optional parameters you I'd do something like Guid? in the parameters but visual studios doesn't like that and I can't do Guid application = null either. Any Ideas?
As you already mentioned, make the parameter optional.
public ActionResult Roles(Guid? id) {
if (id == null || id.Value == Guid.Empty) {
return View();
}
var application = new ApplicationStore().ReadForId(id.Value);
return View(application);
}
This also assumes the default convention-based route
"{controller}/{action}/{id}"
Where the id is optional in the route template.
id = UrlParameter.Optional
For example
routes.MapRoute(
name: "SomeName",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Index", id = UrlParameter.Optional }
);
You could potentially just change the Guid parameter to string.
Guid.Empty = 00000000-0000-0000-0000-000000000000 which may cause issues when trying to pass in a null value.
if you switch it to something like this (but still use a Guid):
public ActionResult Roles(string applicationId)
{
if (string.IsNullOrEmpty(applicationId))
{
return View();
}
var application = new ApplicationStore().ReadForId(applicationId);
return View(application);
}
it may side-step the errors you're encountering.

How to validate query string parameters in GET method

I have built an ASP.NET Core MVC application and I have used the default MVC URL routing:
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
I want to create the GET method for creating and editing the user.
For creating new user I need to go to url /Admin/User and for editing the existing user url /Admin/User/{id:int}
I have tried to create the method like this:
[HttpGet]
public async Task<IActionResult> User(int? id)
{
}
How can I restrict type of the id parameter?
I need only allow access to /Admin/User or /Admin/User/{id:int} - but if I try i.e. - /Admin/User/ABCD - (use an string as ID) - it is allowed as well.
If the parameter ID will be another type instead of number then I want to return 404.
The simplest option is to create a route with this constraint. Note that if you make the parameter required in the route like this, there is no reason to make id nullable.
[HttpGet("Admin/User")]
public async Task<IActionResult> Add()
{
}
[HttpGet("Admin/User/{id:int}")]
public async Task<IActionResult> Edit(int id)
{
}
Or keep the action methods named User and use:
app.UseMvc(routes =>
{
routes.MapRoute("AdminUser", "Admin/User/{id:int}");
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
Note that the convention-based approach doesn't differentiate between GET/POST.
Alternatively, if you want to make all of your ID parameters an int, you could configure the routing for the whole site like this:
routes.MapRoute(
name: "defaultWithId",
template: "{controller}/{action}/{id:int}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
Reference: Routing in ASP.NET Core
You would have to write a custom model binder for this, or create an overload for every other type which returns the 404. I would simply rethink your design and have a different /Admin/CreateUser action for creating a new user instead and just not allow the User call without an ID.
It is possible to achieve using single method.
If you are using numeric ids then for existing user id will be greater than zero. For new user Id will be zero or less than zero.
If you are using string ids then for existing user, id will be non-empty string. E.g. ABCD. For new user Id will be empty.
Based on this logic you can you can send appropriate response or 404 response.
If you are using string Ids, then change the data type of Get method. Instead of using nullable int use string.
Following code might help
[HttpGet("{id}")]
public async Task<IActionResult> User(string id)
{
if (string.IsNullOrWhiteSpace(id))
{
//Replace NewUser view name by Appropriate View name in your project
return View("NewUser");
}
else
{
var isValidUser= IsValidUser(id);
if(isValidUser)
{
//Replace ExistingUser view name by Appropriate View name in your project
return View("ExistingUser");
}
else
{
//User Appropriate overload of NotFound
return NotFound();
}
}
}
private bool IsValidUser(string userId)
{
//Your logic to validate the existing user.
}

How can i have multiple GET methods in single Controller

namespace EmployeeApi.Controllers
{
public class EmployeeDetailsController : ApiController
{
// GET api/employeedetails
public IEnumerable<Employee> Get()
{
}
public IEnumerable<Details> Get(int id)
{
}
public IEnumerable<Team> GetTeamMember()
{
}
public IEnumerable<Details> GetTid(int id)
{
}
}
I would like to have my webApi something like this:
1) IEnumerable<Employee> Get() -> api/employeedetails
2) IEnumerable<Details> Get(int id) -> api/employeedetails/id
3) IEnumerable<Team> GetTeamMember() -> api/employeedetails/id/teammember
4) IEnumerable<Details> GetTid(int id) -> api/employeedetails/id/teammember/tid
I tried making changes to routing, but as I am new to it, could'nt understand much.So, please can some one help me understand and guide me on how this should be done.
Thanks in advance..:)
You could do this with Attribute Routing.
I prefere to use them as they give an easy overview on how the routing is configured when reading the controllers method.
namespace EmployeeApi.Controllers
{
public class EmployeeDetailsController : ApiController
{
// GET api/employeedetails
[Route("api/employeedetails")]
[HttpGet]
public IEnumerable<Employee> Get()
{
}
// GET api/employeedetails/1
[Route("api/employeedetails/{id}")]
[HttpGet]
public IEnumerable<Details> Get(int id)
{
}
// GET api/employeedetails/id/teammember
[Route("api/employeedetails/id/teammember")]
[HttpGet]
public IEnumerable<Team> GetTeamMember()
{
}
// GET api/employeedetails/id/teammember/1
[Route("api/employeedetails/id/teammember/{tid}")]
[HttpGet]
public IEnumerable<Details> GetTid(int tid)
{
}
}
You can also use RoutePrefix on top of the controller that specifies the prefix for the controller route, in your case the "api/employeedetails". You can find more details in the "Route Prefixes" section in the link
After the list of relevant comments has grown, I'll restructure my original answer now.
If you're not able to use attribute routing as suggested in Marcus' answer (see my update statement at the bottom), you need to configure your routes (probably in the App_Start/RouteConfig.cs file). You can try the following code there:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
name: "GetEmployeeDetails",
url: "api/employeedetails",
defaults: new { controller = "EmployeeDetails", action = "GetEmployees" }
);
routes.MapRoute(
name: "GetEmployeeDetailsById",
url: "api/employeedetails/{employeeId}",
defaults: new { controller = "EmployeeDetails", action = "GetDetails", employeeId = UrlParameter.Optional }
);
routes.MapRoute(
name: "GetTeamMember",
url: "api/employeedetails/{employeeId}/teammember",
defaults: new { controller = "EmployeeDetails", action = "GetTeams", employeeId = UrlParameter.Optional }
);
routes.MapRoute(
name: "GetTeamMemberById",
url: "api/employeedetails/{employeeId}/teammember/{teamId}",
defaults: new { controller = "EmployeeDetails", action = "GetDetailsForTeam", employeeId = UrlParameter.Optional, teamId = UrlParameter.Optional }
);
}
}
There'll probably be more routes (for example a generic default route) and also routes to be ignored, but this is out if scope for this question.
These routes correspond with the following action methods within your controller class:
public class EmployeeDetailsController : Controller
{
public IEnumerable<Employee> GetEmployees()
{
// Get your list of employees here
return ...;
}
public IEnumerable<Detail> GetDetails(int employeeId = 0)
{
// Get your list of details here
return ...;
}
public IEnumerable<Team> GetTeams(int employeeId = 0)
{
// Get your list of teams here
return ...;
}
public IEnumerable<Detail> GetDetailsForTeam(int employeeId = 0, int teamId = 0)
{
// Get your list of details here
return ...;
}
}
There is a chance that you do not need the employeeId parameter for the GetDetailsForTeam() method, since maybe the teamId is sufficient to get the desired information. If that is the case you can remove the parameter from the action method and the corresponding route.
These route configurations are pretty straightforward. Each route needs a unique name, otherwise you'll end up with a runtime error. The url - well - contains the url that route is supposed to handle. And after that you can specify the controller name, the action method to be called (these are your Get methods) and their respective parameters.
A word or two regarding naming conventions: In a controller named EmployeeDetailsController I would expect every "generically named" action method to return one or many EmployeeDetails objects (or their respective ActionResults). Therefore, a simple Get() method should return one or many EmployeeDetails objects.
In case you want to return objects of different types I would choose specific names (as suggested in my code above). In your case that would be a GetEmployees() method, a GetDetails(int employeeId = 0) method, a GetTeams(int employeeId = 0) method and a GetDetailsForTeam(int employeeId = 0, int teamId = 0) method. Note the optional parameters here.
If you have these methods in place I'd start with the routing. You need to make sure that each route can be connected to exactly one action method; that's why I asked for the complete URL in one of the comments. If you keep getting the "multiple actions were found" error, you're route URLs are not configured in such a way.
Also please note that route order does matter, though in your example I don't see any conflicting routes.
UPDATE: As an alternative you could use attribute routing, where you put the desired route directly into an attribute of your action method inside the controller. But for this to work with ASP.NET MVC 4 you'd need to install the AttributeRouting NuGet package.

Route is matching on parameter, not action name / View isn't returning properly

I want to create a url like that below:
www.mywebapp.com/Users/Profile/john
I have the UsersController controller and the Profile action, which returns a ViewResult to the Profile page.
I've created a route to manage it:
routes.MapRoute(
name: "ProfileRoute",
url: "Users/Profile/{username}",
defaults: new { controller = "Users", action = "Profile", username = UrlParameter.Optional }
);
The first question is: If i change {username} by {id}, it works. When I put {username} like parameter, the action gets NULL in the parameter. Why this?
Here's my action called Profile:
[HttpGet]
public ActionResult Profile(string id) {
if (UsersRepository.GetUserByUsername(id) == null) {
return PartialView("~/Views/Partials/_UsernameNotFound.cshtml", id);
}
return View(id);
}
I've added a View page to show the user profile. However, when the method ends its execution, I got another error:
The view 'john' or its master was not found or no view engine supports the searched locations.
The following locations were searched:
~/Views/Users/john.aspx
~/Views/Users/john.ascx
...
The second question is: The page I have to show is Profile, not a page with the username's name. Why is it happening?
You are getting this error because you are passing a string (id) to the View function, this overload searches for a view with the name passed in the string (in this case the username).
if you are simply trying to pass the username directly to the view you can use something like a ViewBag, so your code should look like this:
public ActionResult Profile(string id) {
if (UsersRepository.GetUserByUsername(id) == null) {
return PartialView("~/Views/Partials/_UsernameNotFound.cshtml", id);
}
ViewBag.Username=id;
return View();
}
I might be reading this incorrectly, but if you change the name of the required parameter from
id to username, it shouldn't return null
[HttpGet]
public ActionResult Profile(string username) {
if (UsersRepository.GetUserByUsername(username) == null) {
return PartialView("~/Views/Partials/_UsernameNotFound.cshtml", id);
}
return View(username);
}

ASP.Net MVC 4 WebAPI Custom Routing

I have a controller called News in my WebAPI project, I have two default actions called Get that handled the following URL's:
/api/News <- this returns a list of news
/api/News/123 <- this returns a specific news item by id.
All straightforward so far and obviously the default route handles these scenarios. I next want to have a URL that looks like this:
/api/News/123/Artists <- will return all artists related to the specified news item.
Now I am fairly news to ASP.Net MVC and WebAPI so this is the first time I have had to deal with routing. I have modified my default route to look like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{id}/{action}",
defaults: new { controller = "News", action = "Get", id = UrlParameter.Optional }
So here I have moved the {action} to the end of the URL and I have added a Artists method to my News controller. This still works with the first two scenarios but returns a 404 for the third scenario.
Obviously the routing isn't working for /api/News/123/Artists but I have no idea why.
I can't seem to find any examples of people using WebAPI like this which makes me think I am doing something fundamentally wrong.
Any help would be appreciated.
The issue is, that you are trying to acces Web API but mapping the ASP.NET MVC
this is a mapping you need:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{action}",
defaults: new {controller = "News", action = "Get", id = RouteParameter.Optional}
);
And it should be done in the App_Start \ WebApiConfig (if using the default template settings)
Example of the methods (in your news API controller):
// GET api/values/5
public string Get(int id)
{
return "value " + id;
}
// GET api/values/5
[HttpGet]
public string Artist(int id)
{
return "artist " + id;
}
The AttributeRouting should be a good solution. It can be installed by Nuget, and the document is here.
Some examples
public class SampleController : Controller
{
[GET("Sample")]
public ActionResult Index() { /* ... */ }
[POST("Sample")]
public ActionResult Create() { /* ... */ }
[PUT("Sample/{id}")]
public ActionResult Update(int id) { /* ... */ }
[DELETE("Sample/{id}")]
public string Destroy(int id) { /* ... */ }
[Route("Sample/Any-Method-Will-Do")]
public string Wildman() { /* ... */ }
[GET("", ActionPrecedence = 1)]
[GET("Posts")]
[GET("Posts/Index")]
public ActionResult Index() { /* ... */ }
[GET("Demographics/{state=MT}/{city=Missoula}")]
public ActionResult Index(string state, string city) { /* ... */ }
}
It works very well about custom routing.
Update
In asp.net WebApi 2, AttributeRouting is included inside by native. It has some history, the first version, asp.net WebApi 1, is weak about routing annotations.
And then, asp.net WebApi 2 is released, the AttributeRouting is included by native. So, that open project is not maintained anymore, said in GitHub page.
In microsoft blog, the section Independent Developer Profile – Tim McCall – Attribute Routing in MVC and Web API 2 said about the history too.
In routing Action is the action name on the method that you want to route to .That action name should be in the attribute used on the method.
[HttpGet]
[ActionName("CustomAction")]
public HttpResponseMessage MyNewsFeed(Some params)
{ return Request.CreateResponse(); }
Now your route should look like this
routes.MapRoute(
name: "Default",
url: "{controller}/{id}/{action}",
defaults: new { controller = "News", action = "CustomAction", id = UrlParameter.Optional
Let me know if this helps.

Categories