How can i map all actions to C# class not ASP? - asp.net-mvc

I want map all urls like /Home/User or /Home/About or /Home/Register or ... to c# page like this:
for example User.cs Page is like this:
public class User
{
public string run(UrlParameter id){
return "Hello World";
}
}
i want when user send request for /Home/User .. Call Run function of User Class and show return value to user. How can i do that in ASP MVC?
can i do this with change routes in RouteConfig? now current of my MVC routes is:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
and when i call some url program run an asp page in view folder as default of MVC Project in c#.net.
For more explains:
I have protocol between my client side and server side program that is JSON. i want return string JSON when client ask something and for do it i do not need to asp page for rendering html, i only need to call some function that return JSON to client.
How can i do that with MVC?

I'm assuming your question has two parts.
For the first part : Mapping a url to a page. This is in a sense what routing is. It maps a url to an action, which could be a page or maybe a resource like a picture, or a response like JSON data. Notice it's not always a page, generally a url maps to a resource.
Read the URL routing docs here:
routes.MapRoute(
name: "Default",
url: "/Page1",
defaults: new { controller = "Home", action = "Page1",
id = UrlParameter.Optional }
);
In the above example : fakedomain.com/Page1 will run the Page1 method on the HomeController class and if there isn't any code you've added in there it will search for a Page1.aspx or Page1.cshtml inside your views folder.
I would recommend at this point reading about REST. I suggest this article : How I explained REST to my wife
For your second part : How do you return JSON data. Well you use WebApi. See the docs here.
WebApi allows you to write controllers that return data based on the request. So if your client sends an Ajax request with accept headers set to application/json, WebApi will return JSON.
Also it follows the typical system of asp.net-MVC's controllers, routes and actions.
So to return JSON data that represents products you would have a ProductController that looks like this:
public class ProductsController : ApiController
{
Product[] products = new Product[]
{
new Product { Id = 1, Name = "Tomato Soup",
Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo",
Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer",
Category = "Hardware", Price = 16.99M }
};
public IEnumerable<Product> GetAllProducts()
{
return products;
}
public Product GetProductById(int id)
{
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return product;
}
}
With asp.net-mvc4 and the default routing setup for WebApi the above controller would respond to the following URLs
This would get all products
/api/products/
This would get call the GetProductById and return ONE product:
/api/products/put_id_here
I would highly recommend getting all the pre-requisites like visual studio and asp.net-mvc from Web Platform installer and then follow this tutorial.

Related

How to invoke WebApi Controller through URL in ASP.Net MVC Project

How to invoke the WepApi Controller Action in browser URL, Below is the Action Method in APIController.
public virtual HttpResponseMessage Export(string ABC, string product, string Release, bool includeInheritedData = false)
{
}
you can define route path and name for web api methods. Using that route can access a API controller action if it is simple Get call and has Anonymous access.
For example:
Suppose this a method in your API controller:
[HttpGet,AllowAnonymous][Route("api/register")]
public void Register()
{
}
You can access it in the URL like: localhost/api/register from browser.This is a simple example to explain thing in simple terms. There are lot of other things involved accessing API methods depending upon various factors like security, requirements etc.
Already its predefined in Appstart/WebApiConfig.cs for WebApi controllers they were using like below.
config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = #"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
config.Routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
config.Routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new { action = "Post" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) });

Redirect with ASP.NET MVC MapRoute

On my site, I have moved some images from one folder to another.
Now, when I receive a request for old images '/old_folder/images/*' I want to make a permanent redirect to new folder with these images '/new_folder/images/*'
For example:
/old_folder/images/image1.png => /new_folder/images/image1.png
/old_folder/images/image2.jpg => /new_folder/images/image2.jpg
I have added a simple redirect controller
public class RedirectController : Controller
{
public ActionResult Index(string path)
{
return RedirectPermanent(path);
}
}
Now I need to setup proper routing, but I don't know how to pass the path part to the path parameter.
routes.MapRoute("ImagesFix", "/old_folder/images/{*pathInfo}", new { controller = "Redirect", action = "Index", path="/upload/images/????" });
Thanks
I would do in next way
routes.MapRoute("ImagesFix", "/old_folder/images/{path}", new { controller = "Redirect", action = "Index" });
and in controller like that
public class RedirectController : Controller
{
public ActionResult Index(string path)
{
return RedirectPermanent("/upload/images/" + path);
}
}
first download and install RouteMagic package from this link , then redirect your old address to the new address Like the below code :
var NewPath = routes.MapRoute("new", "new_folder/images/{controller}/{action}");
var OldPath = routes.MapRoute("new", "old_folder/images/{controller}/{action}");
routes.Redirect(OldPath ).To(NewPath );
for more information please check out the following link
Redirecting Routes To Maintain Persistent URLs
Answer above using RouteMagic is a good idea, but the example code is wrong (it's included in Phil's post as a bad example).
From the RouteMagic Github demo site global.asax.cs:
// Redirect From Old Route to New route
var targetRoute = routes.Map("target", "yo/{id}/{action}", new { controller = "Home" });
routes.Redirect(r => r.MapRoute("legacy", "foo/{id}/baz/{action}")).To(targetRoute, new { id = "123", action = "index" });
If you specify two routes, you will be setting up an extra mapping that will catch URLs which you don't want.

MVC Routing to use optional subfolder (like a set of virtual directories)

Here's the problem that I have been tearing hair out over since Friday.
I have a single MVC application that serves several different subgroups for a single client. For branding and for some style elements, the Url's need to be formatted like:
www.site.com/Login
www.site.com/Client1/Login
www.site.com/Client2/Login
www.site.com/Client3/Login
and so on.
We would also like to maintain this structure, moving onto www.site.com/Client1/News, etc.
Static routing is off the table. Even a tool to generate them. The site will have X pages with a unique route for Y clients, and I shudder to think about the performance. And because of teh dynamic nature, creating virtual dirs on the fly is not a route I want to travel down.
At first the solution seemed trivial. I tried two test solutions.
The first derived from CustomRouteBase and was able to determine the correct route in the overridden GetRouteData method and then generate the correct Url using GetVirtualPath. The second solution used constraints to see if a client was in the pattern and route accordingly. Both would hit the correct controllers and generate correct links.
Then I added areas (this is just a prototype, but the real site uses areas).
Both solutions failed. The areas were registered properly and worked as they typically should. But with the first solution, I could not find a way to override GetVirtualPath for area registration. I know there is an extension methods off the Area class, but this doesn't fit what I need.
I also tried using a constraint, but the "client" part of the Url was not being added to any of the action links to the areas and trying to route to a controller using a constraint gave me the good old view not found error (searching from the root even though I had specified the area in the route).
So my first question is am I going about this the wrong way? If there is a better way to accomplish this, I am all eras. If not, then how do I manage areas?
I could put some code up here, but it all works for what I want it to do. I'm sort of lost at how to approach the area issue. But unfortunately as always I inherited this project 3 months before launch and my team simply doesn't have the resources to restructure the site without them.
#Max... I tried something similar, but thh areas still would not display their links correctly when in www.site.com/Client1... This is from prototype 6, I tried a few different ways.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//throws view engine can't find view error. If I use www.website.com/Client1, hvering over area links does not give the correct path
routes.MapRoute("Area",
"{folder}/{area}/{controller}/{action}/{id}",
new { area = "Authentication", controller = "Authentication", action = "Index", id = UrlParameter.Optional },
new { folder = new IsFolderContsraint(), area = new IsArea() }
);
//works fine.. if I use www.website.com/Client1, hovering over regular (non area) links works fine
routes.MapRoute("Branded",
"{folder}/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { folder = new IsFolderContsraint() }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
Here is another way I tried to tackle it. Please don't crack on the implementation, it's just a POC. The problem here was that Areas are not part of RouteBase, so I can't modify the virtual paths for them. So every action link, etc, get's rendered correctly and all works well as long as the action is not in an area.
public class Folders
{
private static Dictionary<string, string> _folders= new Dictionary<string, string>()
{ {"test1", "style1"},
{"test2", "style2"},
{"test3", "style3"}
};
public static Dictionary<string, string> FolderNames { get { return _folders; } }
}
public class AreaDefinitions
{
private static Dictionary<string, string> _areas = new Dictionary<string, string>()
{ {"Authentication", "Authentication"} };
public static Dictionary<string, string> AreaDefinition { get { return _areas; } }
}
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(new CustomRouteBase());
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
public class CustomRouteBase : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
//Creates routes based on current app execution pass
//a bit like doing my own routing constraints, but is more dynamic.
//get route data (and CreateVirtualPath) will be called for any action needing to be rendered in the current view.
//But not for areas :( Ugly but just a prototype
string url = httpContext.Request.AppRelativeCurrentExecutionFilePath;
url = url.StartsWith("~/") ? url.Substring(2, url.Length - 2) : url;
string[] urlParts = url.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
RouteData rd = new RouteData(this, new MvcRouteHandler());
if (urlParts.Length == 0)
{
rd.Values.Add("controller", urlParts.Length > 0 ? urlParts[0] : "Home");
rd.Values.Add("action", urlParts.Length > 1 ? urlParts[1] : "Index");
return rd;
}
if (Folders.FolderNames.ContainsKey(urlParts[0]))
{
if (urlParts.Length > 1 && AreaDefinitions.AreaDefinition.ContainsKey(urlParts[1]))
{
rd.DataTokens["area"] = urlParts[1];
rd.Values.Add("controller", urlParts.Length > 2 ? urlParts[2] : "Home");
rd.Values.Add("action", urlParts.Length > 3 ? urlParts[3] : "Index");
}
else
{
rd.Values.Add("controller", urlParts.Length > 1 ? urlParts[1] : "Home");
rd.Values.Add("action", urlParts.Length > 2 ? urlParts[2] : "Index");
}
rd.DataTokens.Add("folder", urlParts[0]);
}
else
{
rd.Values.Add("controller", urlParts.Length > 0 ? urlParts[0] : "Home");
rd.Values.Add("action", urlParts.Length > 1 ? urlParts[1] : "Index");
}
return rd;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//Assembled virtial path here using route and values.
//Worked (ugly but functioned, but was never called for areas.)
string url = "";
if (requestContext.RouteData.DataTokens.ContainsKey("folder"))
url += requestContext.RouteData.DataTokens["folder"] + "/";
if (values.ContainsKey("controller"))
url += values["controller"] + "/";
if (values.ContainsKey("action"))
url += values["action"];
var vpd = new VirtualPathData(requestContext.RouteData.Route, url);
return vpd;
}
}
Thanks Liam... that's similar to how our view engine is customized, to read from a "plugins" folder first for view overrides. But the problem is a little different here. This is one client that already has a site with view overrides, but in turn has multiple clients of their own. The behavior here is just to control style sheets, logos, etc. After a user is logged in, I can identify what the style and branding should be, but for landing pages the client wants to use a(n) url like "www.site.com/Client1" to identify this. I've given up and written a handler that just turns the request into www.site.com/?client=client1 so I can handle landing page styling, but it would be so much nicer to leave the url as www.ste.com/Client1/login, etc. This is a conversion from a classic asp app that used vdirs to host different directories prior to this. Because of the number of potential clients (100's), static routing gets heavy. My solutions all work to a point... it's the areas that are causing all the problems. If I could just find a way to remap their virtual paths dynamically like I can with the routes I create in RouteBase, I would be in business... I think.

ASP.NET MVC Areas in Individual Projects - Refactor AreaRegistration Stuff

I'm trying to molularize my ASP.NET MVC application by moving each Area into their own project. Everything was working fine until i decided to refactor out the AreaRegistration stuff and use my own approach (This way i can also register filters and dependencies within my module). Using reflector i have managed to come up with the following.
First i implement the following interface for each module/area:
public interface IModule {
string ModuleName { get; }
void Initialize(RouteCollection routes);
}
E.g.:
public class BlogsModule : IModule {
public string ModuleName { get { return "Blogs"; } }
public void Initialize(RouteCollection routes) {
routes.MapRoute(
"Blogs_Default",
"Blogs/{controller}/{action}/{id}",
new { area = ModuleName, controller = "Home", action = "Index",
id = UrlParameter.Optional },
new string[] { "Modules.Blogs.Controllers" }
);
}
}
Then in my Global.asax file (Application_Start event) i say:
// Loop over the modules
foreach (var file in Directory.GetFiles(Server.MapPath("~/bin"), "Modules.*.dll")) {
foreach (var type in Assembly.LoadFrom(file).GetExportedTypes()) {
if (typeof(IModule).IsAssignableFrom(type)) {
var module = (IModule)Activator.CreateInstance(type);
module.Initialize(RouteTable.Routes);
}
}
}
I then removed the existing AreaRegistration stuff. Everything is working fine up to this point. When i run my application and render the link to a module, e.g.:
#Html.ActionLink("Blogs", "Index", "Home", new { area = "Blogs" }, null)
The correct url is displayed but when i click on the url it displays the wrong view. After debugging it looks like the url is routed to the correct Action within the HomeController of my Blogs module. However it tries to display the Home/Index.cshtml view in the main project and not the one in the module/area. I'm guessing somewhere along the lines i have missed how to tell the view engine to treat the routed url as an area as it seems to be ignoring the AreaViewLocationFormats (inside the RazorViewEngine).
I'd appreciate it if someone could show me what i'm missing. Thanks
After further refactoring it appears that, the view engine looks for an area data token. I therefore changed the code to add routes in Initialize method of the module as:
// Create the route
var route = new Route("Blogs/{controller}/{action}/{id}", new RouteValueDictionary(new { area = ModuleName, controller = "Home", action = "Index", id = UrlParameter.Optional }), new MvcRouteHandler());
// Add the data tokens
route.DataTokens = new RouteValueDictionary();
route.DataTokens["area"] = this.ModuleName;
route.DataTokens["UseNamespaceFallback"] = false;
route.DataTokens["Namespaces"] = new string[] { "Modules.Blogs.Controllers" };
// Add the route
routes.Add(route);
Hope this helps.

ASP.net MVC Areas and creating an ActionLink with ID (SEO / clean URL)

I am building a Help Desk Ticket system for a client using ASP.NET MVC 1.0 / C#. I have implemented Steven Sanderson's "App Areas in ASP.NET MVC, Take 2" and it is working great.
In my Globabl.asax page I have some routes defined as such:
public static void RegisterRoutes(RouteCollection routes)
{
// Routing config for the HelpDesk area
routes.CreateArea("HelpDesk", "ProjectName.Areas.HelpDesk.Controllers",
routes.MapRoute(null, "HelpDesk/{controller}/{action}", new { controller = "Ticket", action = "Index" }),
routes.MapRoute(null, "HelpDesk/Ticket/Details/{TicketId}", new { controller = "Ticket", action = "Details", TicketId = "TicketId" })
);
}
So, if I enter "http://localhost/HelpDesk/Ticket/Details/12" in the browser address bar manually, I get the results I expect. Here is my controller:
public ActionResult Details(int TicketId)
{
hd_Ticket ticket = ticketRepository.GetTicket(TicketId);
if (ticket == null)
return View("NotFound");
else
return View(ticket);
}
In my view I have:
<%= Html.ActionLink(item.Subject, "Details", new { item.TicketId } )%>
But that code generates "http://localhost/HelpDesk/Ticket/Details?TicketId=12" which also returns the expected results. My Question is...
How do I define an ActionLink when using Steven Sanderson's Areas that will create a clean URL like: "http://localhost/HelpDesk/Ticket/Details/12" ?
Try
<%= Html.ActionLink(item.Subject, "Details", new { TicketId = item.TicketId } )%>
The ActionLink method expects a dictionary with keys that match the parameter names. (Note that passing an anonymous object is a convenience for this). Anything else I believe it will just tag onto the end of the URL.
EDIT: The reason that this isn't working for you is because your first route matches and takes precedence (controller and action), but defines no TicketId parameter. You need to switch the order of your routes. You should always put your most specific routes first.
Try
<%= Html.ActionLink(item.Subject, "Details", new { TicketId=item.TicketId } )%>
I think Womp has it ...
Oh and while you are swapping your routes try
routes.MapRoute(null, "HelpDesk/Ticket/Details/{TicketId}", new { controller = "Ticket", action = "Details"})
I think the , TicketId = "id" is messing things up
Hope that helps,
Dan

Resources