Generate dynamic route path in asp.net core - c#

I have created one .net core application that has below path for crud operations.
Create: http://localhost:1001/admin/123/app/456/user/Create
Update: http://localhost:1001/admin/123/app/456/user/Update
Select: http://localhost:1001/admin/123/app/456/user/Select
Writing below will solve the issue in the URL.
routes.MapRoute("CreateUser", "apps/{appId}/user/create",
defaults: new { controller = "User", action = "Create" });
How to include the same in .cshtml file i.e
<a asp-action="Create" asp-controller="User"> Create User </a>
The quick help appreciated.

You can use asp-route-{value} option to indicate route values in anchor tag helper:
<a asp-action="Create" asp-controller="User" asp-route-adminID="#Model.AdminId" asp-route-appId="#Model.AppId"> Create User </a>
Please consider that I assumed that adminId and appId values are provided in the Model of the page. If it's wrong, set appropriate variable instead of those in your code.
For more information, you can check the Microsoft documentation:
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/anchor-tag-helper?view=aspnetcore-3.0#asp-route-value

Related

Change route string url of blog from controller/action/{*Url} to {*Url}

I have my personal blog site which was done using Asp.Net MVC and C# language.
As I have configured RouteConfig with "controller/action/{*Url}" for blog URL where {*Url} could be any text.
Now I want to change the Url pattern from "controller/action/{*Url}" to {*Url}.
Example:
From below Url
https://abcd.co.in/home/newsdetail/abcd-xyz-opqr-stu
To
https://abcd.co.in/abcd-xyz-opqr-stu
I have done the following changes
routes.MapRoute("", "mycontroller/myactionmethod/{*Url}", new { controller = "Home", action = "NewsDetail", Url = "" });
But it is calling every time URL hits my domain.
Please suggest what are the possible solutions that will not affect other functionality or URLs and it should fulfill my requirements.
Thanks in advance.
After a few google search and R&D on Attribute-based routing I have come up with one solution:
Steps:
Need to define that I am using Attribute routing
routes.MapMvcAttributeRoutes();
Remove existing route entry for the Action method.
Set Attribute route entry path above the Action method.
[Route("{Url}")]
Just update the URL in href from previous to just URL.
From
<a href="/home/newsdetail/#item.Url">
To
<a href="/#item.Url">

ActionLink and relative path

I can't find out how to solve this. I have two URLs. These are /my-url-1 and /my-url-2. Both going to different views.
The thing is that I have an ActionLink on /my-url-1's view which should make /my-url-2 and go to that view.
The problem is that ActionLink makes /my-url-1/my-url-2 as the URL and not just /my-url-2.
I was searching two days about how to fix it but couldn't find anything.
PD: I'm not using Areas so please don't tell me that I just should put the "area" parameter as a "".
These are two urls which goes to different controllers and different actions.
View which has the ActionLink (URL:/my-url-1) :
<div class="btn-index-container">
#Html.ActionLink("Url 2", "MyAction", "MyController")
</div>
This ActionLink should render:
Url 2
But it's rendering:
Url 2
where /my-url-1 is my current URL
Route Config
routes.MapRoute(
name: "route1",
url: "my-url-2", //without parameters
defaults: new { controller = "MyController", action = "MyAction" },
);
routes.MapRoute(
name: "route2",
url: "my-url-1", //without parameters too
defaults: new { controller = "MyController2", action = "MyAction2" }
);
So, when I go to localhost:port/my-url-1 it loads MyAction2 which renders a view. This view has inside an ActionLink(described above) which should render a /my-url-2 link.
Well, I've worked inside the MVC framework and I could told you about how Url.RouteUrl or Html.RouteLink works. At the end, the method which create the URL is GetVirtualPathForArea (this method is called before UrlUtil.GenerateClientUrl, which receive the VirtualPathData.cs created by GetVirtualPathForArea, as a parameter) from System.Web.Routing.RouteCollection.cs.
Here I left a link to the MVC source code:
https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Web.Mvc
I found that, my Request.ApplicationPath was changing when I loaded /my-url-1. It was crazy because the application path was /.
At last, the problem was that the /my-url-1 was pointing to a virtual directory created on the IIS some time ago by error.
To know where your IIS configuration file is, please follow the link below:
remove virtual directory in IIS Express created in error
The solution was remove the .vs directory (which contains the config .vs\config\applicationhost.config) and rebuild
I think most of the Helpers that render URLs works more or less in the same way, so I hope it'll useful for all of you!
In your case, maybe no its necesary, just pass the parameters with null values, E.G.
#Html.ActionLink("EspaƱol", null, null, new { Lng = "es" }, null)
In this way, the parameters change, and the view is relative, depending on where you are.

Argument passed to controller is always null

I've been working on a relatively simple application in ASP.NET Core that displays the status of various nodes in a network. It displays their latest status, and some other information.
For this, I've made a controller which has an action that takes a node's name, performs a lookup in the node manager, and returns the detail view for that specific node. This controller action is implemented like so:
public IActionResult Detail(string name)
{
// Maybe redirect this to the node detail overview once it's done?
if (string.IsNullOrEmpty(name))
return View("PageNotFound");
var viewModel =
Current.NodeManager.Statuses.FirstOrDefault(s => s.Node.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (viewModel == null)
return View("PageNotFound");
return View(viewModel);
}
I'm linking to this action from my overview page. The HTML I'm using to do this is:
<a asp-area="" asp-controller="Node" asp-action="Detail" asp-route-id="#status.Node.Name">#status.Node.DisplayName</a>
For one of my nodes, this produces the following link:
http://example.com/Node/Detail/Temeria
And from what I understand from the ASP.NET Core documentation, the controller should capture "Temeria" here as the argument for the Detail action of NodeController, but it reliably refuses to do so. name here is always null.
I've also messed around with the routing in Startup.Configure, by adding the following route to the controller:
routes.MapRoute(
name: "Node Detail",
template: "{controller=Node}/{action=Detail}/{name}");
But unfortunately, to no avail. Every time I invoke the action, be it via clicking the link I've outlined above, or visiting the detail action manually by typing in the URL in my browser, ASP.NET Core spits out the following log line:
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method Aegis.Controllers.NodeController.Detail (Aegis) with arguments () - ModelState is Valid
I'm at a loss here - am I missing something really obvious here, or does ASP.NET Core not work with primitive types and should I resort to the "model binding" I've been seeing in the guides (which seems to be a bit overkill for an action as simple as this one)?
I've seen various other questions similar to this one, but none of them were for ASP.NET Core. I've done similar projects in classic ASP.NET, and never had this issue with that framework.
Any help would be greatly appreciated. Thanks for your time!
The key is in this line:
<a asp-area="" asp-controller="Node" asp-action="Detail" asp-route-id="#status.Node.Name">#status.Node.DisplayName</a>
I've changed asp-route-id to asp-route-name and everything works fine. So the answer is:
<a asp-area="" asp-controller="Node" asp-action="Detail" asp-route-name="#status.Node.Name">#status.Node.DisplayName</a>

route not mapping mvc 3

I am new in mvc and I have a situation where I am convinced that I am mapping a route correctly although it is not.
it is a very basic login form with the option of passing in parameters.
this is the html
<li>Login</li>
and this is the action method in the 'Home' controller
public ViewResult LoginForm(string userName)
{
return View();
}
This is how is my attempt at mapping the route
routes.MapRoute(
null,
"Login/{userName}",
new { controller = "Home ", action = "LoginForm", UrlParameter.Optional }
);
The url is however displaying as follow
/Home/LoginForm?loginUser=user
my aim would be the following
Login/user
Advice perhaps as to why it is not mapping correctly. I have already registered a number of routes in the Global.asax.cs file. Could it have something to do with the order with which they were registered?
Try this:
<li>Login</li>
change the parameter loginUser to userName.
Use userName instead of loginUser
<li><a href='#Url.Action("LoginForm", "Home", new {userName="user"})'>Login</a></li>
You are hitting a different address than the one specified in MapRoute. The mapped route will not fire. Change both the parameter and the action name.
<li>Login</li>
You need to access /Home/Login not /Home/LoginForm. The routing is done automatically if the right address is accessed.
EDIT:
Following your address edit:
As far as I know, you cannot generate a link such as Login/{userName} using Url.Action; if you don't specify a controller, this defaults to Home controller
You can however access the Login/{userName} link directly from the browser (due to the mapped route)
You can create a "static" (i.e. classic) link, passing a hard-coded address:
<li>Login</li>
Please note that the userName added/removed per JavaScript.

Dynamic URL route

This app has several routes configured in RouteConfig.cs. For instance, I have the two following routes defined:
routes.MapRoute(
name: "MyPage-Demo",
url: "pages/page-title/demo",
defaults: new { controller = "Root", action = "PageDemo" }
);
routes.MapRoute(
name: "MyPage",
url: "pages/page-title/{resource}",
defaults: new { controller = "Root", action = "Page", resource = UrlParameter.Optional }
);
Each page someone visits has a link to a "demo". A page could be accessed by visiting http://localhost/pages/page-title. This works fine.
When a user clicks the "demo" link, they are redirected to a page located at http://localhost/pages/page-title/demo. This works fine.
My problem is the demo page may reference a complex nested structure. The structure consists of JavaScript, css, images, etc. Content used for the purpose of the demo. None of these nested resources can be found. However, I'm not sure how to setup my routing to account for these nested files.
I'm confident I'm going to need to update my controller's PageDemo action. However, I'm not sure
a) how to do so in a way that will allow for differing structures and
b) how to update my route configuration to account for these nested structures.
Is there a way to do this? In reality, I'm going to have multiple pages and multiple demos. For that reason, I want to have something a little more reusable than a hard-coded approach.
If you just need to serve files physically stored in a path, you should be able to just ignore the route, e.g.:
routes.IgnoreRoute("pages/page-title/demo/resources/{*resource}");
That will bypass MVC trying to route the request to a controller.
Or you could go by file extension:
routes.IgnoreRoute("{file}.js");
routes.IgnoreRoute("{file}.css");
(Code is untested, but it looks like you're trying to do something similar here :)
https://stackoverflow.com/a/3112192/486620
IF I understand:
The problem seems to be that your MyPage-Demo route:
routes.MapRoute(
name: "MyPage-Demo",
url: "pages/page-title/demo",
defaults: new { controller = "Root", action = "PageDemo" }
);
is NOT {resource} specific, while your MyPage route IS.
If you change your route to take a {resource}
routes.MapRoute(
name: "MyPage-Demo",
url: "pages/page-title/demo/{resource}",
defaults: new { controller = "Root",
action = "PageDemo", resource = UrlParameter.Optional });
Then your action method can
return specific Views with proper resource settings
set a Viewbag property with path to your specific resource
If this is inline with your intent, these routes can be consolidated into
routes.MapRoute(
name: "MyPage-Demo",
url: "pages/{action}/{resource}",
defaults: new { controller = "Root",
action = "PageDemo", resource = UrlParameter.Optional });
/pages/PageDemo/{resource} resolves to Controller=pages, action = PageDemo
/pages/demo/{resource} resolves to Controller=pages, action = demo.
This convention allows you flexibility to create more {resource} dependant links
In the Browser, Right Click Demo page => Choose View Page Source.
Here, you have the link for the CSS and Js files in your Demo page. Click on those js/css file links. Check if there are redirecting you to the correct/expected location. Otherwise you could make the Css/Js file URL accordingly Because, as per the demo page each PageDemo will have its own unique structure of JS/Images/css, etc
How are you referencing your JS and CSS files ?
If you use the tilde character like : ~/Content/Styles/Site.css you won't have any problem no matter where you are in your virtual path.
Also not 100% sure I am directly answering your question, but making the assumption that the resources you are trying to access are nested in a folder structure that mirrors the page structure - and the issue you are having is how to ignore the routes to these without having to know what they might be in advance?
This does a good job of explaining that: https://stackoverflow.com/a/30551/1803682
I would ask:
As #PKKG notes in his answer - do the links in the page source match what you expect?
How is this per-demo content served: e.g. by a service and not a static file?
this answer contains two approaches. the second one may be more suitable for your scenario. the first may be more suitable for a general mvc project
approach one
i suggest creating a organized structure in your content folder to store the scripts and css files, ie
/Content/Demos/Page-Title-1/
/Content/Demos/Page-Title-2/
/Content/Demos/Page-Title-3/
and
/Content/Demos/Common/
and then make a bundle to render the scripts and css files for each page title
ie.
bundles.Add(new StyleBundle("~/Demo/page-title/css").Include(
"~/Content/Demos/Page-Title-1/csscontent1.css",
"~/Content/Demos/Page-Title-1/csscontent2.css",
"~/Content/Demos/Page-Title-1/csscontent3.css",
"~/Content/Demos/Page-Title-1/csscontent4.css"));
bundles.Add(new StyleBundle("~/Demo/page-title/js").Include(
"~/Content/Demos/Page-Title-1/jscontent1.css",
"~/Content/Demos/Page-Title-1/jscontent2.css",
"~/Content/Demos/Page-Title-1/jscontent3.css",
"~/Content/Demos/Page-Title-1/jscontent4.css"));
this will allow you to render the scripts on the demo page using a few line approach, ie.
#Styles.Render("~/Demo/page-title/css");
#Scripts.Render("~/Demo/page-title/jss");
#Styles.Render("~/Demo/common/css");
#Scripts.Render("~/Demo/common/css");
you will have to update the files in global .asax as you change the files in your /Content/Demos/Page-Title/ folder.
there is the benefit that if you choose, you may bundle and minify the files to save bandwidth and load time for the first page load.
approach two.
(still use the following folder structure
/Content/Demos/Common/
and
/Content/Demos/Page-Title-1/
/Content/Demos/Page-Title-2/
/Content/Demos/Page-Title-3/)
make an html helper to reference all the scripts & contents in a folder
its usage would be
#Asset.RenderAssets( '~/folderdirectory')
and the helper would do something like
#helper RenderAssets (stirng directory){
#* scrape the directory for all script files*
var scripts = find all scripts in the directory
#* include the script files *#
for each script
<script src=" ... .js"></script>
#* scrape the directory for all cssfiles*
var styles = all css in the directory
#* include the css files *#
for each style
<link rel='stylesheet' type="text/css" href=" ... .css">
}
this would be a few line usage in each demo view
#Asset.RenderAssets( '~/Content/Demos/Common')
#Asset.RenderAssets( '~/Content/Demos/Page-Title')
you may or may not need to pair this with an extra few line or two in your global.asax or RouteConfig.cs file (see source 3)
routes.IgnoreRoute("/Content/Demos/{page}/{script}.js");
routes.IgnoreRoute("/Content/Demos/{page}/{style}.css");
relevant sources
to create html helpers see
http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx
to use bundling and minifcation (the scripts.render approach) see
http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification
phill haakk says may not need to pair this with an ignore route!
https://stackoverflow.com/a/30551/1778606
commentary and edits are encouraged.
All static content (.js, .css, .html, .png) is not seen by MVC (unless modules/runAllManagedModulesForAllRequests is set to true in web.config). Static content extensions are defined in IIS configuration "module mapping", and is using the StaticFileHandler module (and not the .NET module).
So static content must be referenced by its physical path relative to the current path (the path of the current html page).
The best solution is to use absolute link from the root of the website. Like /content/demo1/demo1.html, put all js,css in /content/demo1/, and in demo1.html use path relative to the /content/demo1/ folder (where the .html is). Ie: with demo1.css being in the same folder.
The link to demo1.html would be demo 1

Categories