Composite C1 routes.MapPageRoute - Directing to a page - asp.net-mvc

I have been adding some custom routes which are not working
I can get this MVC route working but the problem is it simple routes directly to the view rather than the page which contains the master layout etc.
routes.MapRoute("Job-Listing", "job-detail/{category}/{title}/{id}", new { controller = "JobSearchModule", action = "JobDetail" });
I tried routing to a page which existed like following. This didn't work and simple went to a page not found.
routes.MapPageRoute("Job-Listing", "job-detail/{category}/{title}/{id}", "~/job-seekers/job-search/job-detail");
I guessed that this might be because this is not a physical path and there is some other routing going off under the hood. So I tested this by adding a Route to a physical page like follows. (this route worked)
routes.MapPageRoute("jobDetail2Route", "job-detail/{category}/{title}/{id}", "~/Text.aspx");
This got my thinking that composite c1 might have a physical URL which the C1 routing maps to. I'm sure I have seen at some point something to do with a /Renderers/Page.aspx. Does anyone know if I could somehow route to a physical page in this way?
Thanks
David
OK so some further information.
I realized I could get the the URL using /Renderers/Page.aspx?pageId=d622ab3b-2d33-4330-9e6e-d94f1402bc80. This URL works fine so I attempted to add a new route to this URL like as follows...
routes.MapPageRoute("Job-Listing", "job-detail/{category}/{title}/{id}", "~/Renderers/Page.aspx?pageId=d622ab3b-2d33-4330-9e6e-d94f1402bc80");
Unfortunately this still didn't work. I got an error on the Renderers/Page.aspx
Value cannot be null.
Parameter name: pageUrlData
Any ideas?

Rather than using the internal representation of a page URL like ~/Renderers/Page.aspx?pageId=d622ab3b-2d33-4330-9e6e-d94f1402bc80 you should use the public representation like /my/page/url.
To get the public representation from the page id you could use a method like this:
using Composite.Data;
...
public string GetPageUrl( Guid pageId )
{
using (var c = new DataConnection())
{
PageNode p = c.SitemapNavigator.GetPageNodeById(pageId);
return p.Url;
}
}
This will give you the public URL of the page with the provided id. If you are running a website with different content languages this method will automatically fetch you the URL matching the language of the page your MVC Function is running on.
Should you have the need to explicitly change the language of this URL use the overload DataConnection(CultureInfo culture) instead of the default constructor.
With the above method in place you should be able to do this:
routes.MapPageRoute(
"Job-Listing",
"job-detail/{category}/{title}/{id}",
GetPageUrl(Guid.Parse("d622ab3b-2d33-4330-9e6e-d94f1402bc80"));

Related

Area webapi route not being resolved

am trying to test the web api routes, that exists inisde my administration area.
This is the the route definition in the area
[HttpPut]
[Route("timezone/put/{timezone}", Name = "PutTimeZone")]
[AllowAnonymous]
[ResponseType(typeof(void))]
public IHttpActionResult PutTimeZone(string timezone)
{
/*Action body*/
}
I have a route prefix like so [RoutePrefix("admins/misc")]
Here is my route test
const string route = "/admins/misc/timezone/put/-120";
RouteAssert.HasApiRoute(_httpConfiguration, route, HttpMethod.Put);
_httpConfiguration.ShouldMap(HttpMethod.Put, route)
.To<MiscApiController>(HttpMethod.Put, x => x.PutTimeZone("-120"));
When I run the test, I get MvcRouteTester.Assertions.AssertionException : Expected 'Administration', got no value for 'area' at url '/admins/misc/timezone/put/-120'.
I read in the route tester wiki page
If you are using MVC areas, then as long as you use the standard
layout of namespaces, the area name will be extracted from your
controller type name and tested against the area chosen by the route.
e.g. if your controller's fully qualified type name is
MyWebApp.Areas.Blog.CommentController then the expected area name will
be "Blog".
But frankly, it doesn't give me any clue as to what to do so as to make my test pass. My question is what am I missing?
Try replacing [RoutePrefix("admins/misc")] with [RouteArea("Administration",AreaPrefix = "admins/misc")] or combining both together with [RouteArea("Administration",AreaPrefix = "admins"),RoutePrefix("misc")].
Edit:
I downloaded the source for the MvcRouteTester library and tried to debug it using almost the same example as you provided and it looks like there is an issue with the library itself.
Specifics: one method Common.Verifier.VerifyExpectations asserts that the expected area expected.Area matches route's actual area actual.Area, but there is no previous code that sets the actual area property to any value (although it does set Controller and Action properties in ApiRoute.Generator.ReadRequestProperties).
So I suppose that at its current state (at the time of this post) the library simply does not support areas for WebApi. I suggest that you open an issue on the MvcRouteTester github page with a link to this post.

ASP.Net MVC redirecttoaction not passing action name in url

I have a simple create action to receive post form data, save to db and redirect to list view.
The problem is, after redirecttoaction result excutes, the url on my browser lost the action section. Which it should be "http://{hotsname}/Product/List" but comes out as "http://{hotsname}/Product/".
Below is my code:
[HttpPost]
public ActionResult Create(VEmployee model, FormCollection fc)
{
var facility = FacilityFactory.GetEmployeeFacility();
var avatar = Request.Files["Avatar"].InputStream;
var newModel = facility.Save(model, avatar);
return RedirectToAction("List");
}
The page can correctly render list view content, but since some links in this view page use relative url, the functions are interrupted. I am now using return Redirect("/Employee/List") to force the url. But I just wonder why the action name is missing. I use MVC3 and .Net framwork 4.
I am new to ASP.Net MVC, thanks for help.
Your route table definitely says that "List" action is default, so when you redirect to it as RedirectToAction("List") - routing ommits the action because it is default.
Now if you remove the default value from your routes - RedirectToAction will produce a correct (for your case) Url, but you'll have to double check elsewhere that you are not relying on List being a default action.
Well, Chris,
If you get the right content on http://{hotsname}/Product/ then it seems that routing make that URL point to List either indirectly (using pattern like {controller}/{action}) and something wrong happens when resolving URL from route or {action} parameter is just set wth default value List. Both URLs can point to the same action but the routing engine somehow takes the route without explicit action name.
You should check:
Order in which you define your routes
How many routes can possibly lead to EmployeeController.List()
Which one of those routes has the most priority
Default values for your routes
Just make the route with explicit values: employee/list to point to your List action and make sure that is the route to select when generating links (it should be most specific route if possible).
It would be nice if you provide your routes mappings here.
but since some links in this view
page use relative url, the functions
are interrupted.
Why do you make it that way? Why not generate all the links through routing engine?
When using the overload RedirectToAction("Action") you need to be specifying an action that is in the same controller. Since you are calling an action in a different controller, you need to specify the action with the alternate overload e.g. RedirectToAction("List", "Employee").

ASP.NET MVC Action Parameters with Binded prefix not compatible with Url.Route in Global.asax

I have a details page containing a form field named UserId. On the same page i have another search form with a field also named UserId.
I am using Html.LabelFor(vm > vm.UserId) and Html.TextBoxFor(sm > sm.UserId) on the two different view models, vm being the view model and sm being the search model. (Yes, the UserId property on the two models has identical names - because they are the same domain property.
When i navigate to the page, the populated UserId on the vm is inserted into BOTH form fields named UserId by MVC. Even the sm.UserId are empty.
That is my initial problem. There are a few ways ti avoid that. My solution was to use the Prefix flag for the sm.
[HttpGet]
public ActionResult Search([Bind(Prefix = "Search")] SearchFormViewModel searchFormViewModel, PagingViewModel pagingViewModel)
{
This will provoke MVC to render a Search.UserId on the fieldname in the search form, but the property in code will still be named UserId.
This solution seems to work great!
BUT:
Now i have to address the search.UserId on a route from Global.asax.
I map the route like this:
routes.MapRoute(
"MyRouteName",
"ControllerName/User/{Search.UserId}",
new { controller = "ControllerName", action = "Search" }
);
My problem is that MVC can't map the Search.UserId (because of the .) to fit the UserId (prefixed with Search) in the action shown above.
So it seems like MVC has a prefix-feature, that are actually nok fully supported through the Route-handler.
Ofcourse i could rename the Search.UserId to Search_UserId, but then the name dosent match the name MVC expects in the recieving action above. (expects Search.UserId) Renaming The UserId property of the search model would fix the issue, but since it is the same value in the domain, this seems like a workaround.
Am I missing something here about the usage of the Prefix feature or is this just not possible?
So... I've been thinking about this for a while now. - And a colleague of mine suddently showed me the light.
The problem lies where MVC maps the object to a route dictionary.
See the user
wount work. Because MVC can not handle the .(dot) in the object name.
but since the object name is just a string key in the routevaluedictionary, mapping it my self did the trick:
See the user

ActionLink/RouteLink based on URL not Controller

I have a controller that uses the following structure:
.com/Object/375
However, I can also use the following URL when I am accessing special admin rights
.com/Admin/Object/375
I use the same user controls whether you're in the Admin section or not, but they both point to the same Controller Object. I need for the links to maintain that URL structure and not try to kick an Admin user back to the Object controller. I am currently using the route name method, where these are my route names (in global.asax):
"Admin/-Object"
"Object/-Object"
"Object-Object"
These route names catch the following routes:
Admin/Object, Admin/Object/555, Object, Object/323
I then use the following in a route link
Html.RouteLink(id, Request.Url.Segments[1] + "-Object", new { id = id })
This works just fine, but has an odd smell - any other ideas?
To clarify: I need the URL to be properly created based on the current URL structure (with or without the Admin) and the routing will point to the correct controller (the same for both URLs) and the admin specific content will be injected into the page only if in the Admin section (based on URL).
Just to wrap this up, using ViewBag is probably a better idea because using the URL segment might result in unexpected errors, especialy if you move the controls or views around.

Url routing with database lookup?

I want to build a ASP.NET MVC site so that the controller for a specific url is stored in the database instead of the URL.
The reason for that is that i'm building a CMS system and the users should be able to change the template (controller) without changing the URL. I also think that the name of the controller is not relevant for the end users and i want clean URL:s.
I realise that i could just add all routes at application start, but for a system with like 100 000 pages it feels like a bad idea.
Is it possible to store the url:s in the database and make a lookup for each request and then map that request to a specific controller?
Basically you'll have to implement your own IRouteHandler.
Part of the answer and some example code is in Option 3 of this question's answer:
ASP.NET MVC custom routing for search
More information:
http://weblogs.asp.net/fredriknormen/archive/2007/11/18/asp-net-mvc-framework-create-your-own-iroutehandler.aspx
Why couldn't you just do something like this:
-- Global.asax.cs --
routes.MapRoute(null, // Route name
"content/{id}", // URL with parameters
new { Controller = "Content", Action = "Show", Id = (string) null }); // Parameter defaults
-- /Controllers/ContentController.cs --
public class ContentController : Controller
{
public ActionResult Show(string id)
{
// Lookup the 'content' (article, page, blog post, etc) in the repository (database, xml file, etc)
ContentRepository repository = new ContentRepository();
Content content = repository.FindContent(id);
return View(content);
}
}
Such that a request to your site www.yoursite.com/content/welcome-to-my-first-blog-post would call ContentController.Show("welcome-to-my-first-blog-post").
I suppose ASP.NET can do many of the same things as PHP. If so there is a simple approach.
With rewrite rules you can easily send any traffic to any URL of the 100K to the same place. On that destination you could simply use the server variables containing the URL requested by the client and extract the location. Look it up in the DB and send the corresponding data for that URL back to the client on-the-fly.
"for a system with like 100,000 pages it feels like a bad idea."
It is a bad idea if you are creating a routing system that cannot be reused. The basic {controller}/{action}/{id} schema points you in the direction of reuse. This schema can be extended/revamped/recreated according to your needs.
Instead of thinking about how many pages you have think about how your resources can be grouped.
Instead of creating a heavy routing system why not create an anchor link control (ascx) which allows user to only add valid internal links. Keep a table in the db of your templates and their controllers to populate the control with it.

Resources