ASP.Net breadcrumb style URL Routing using webforms - c#

I am working on a ASP.Net website using web-form & C#.
I have used URL Routing to convert my basic url to SEO Freindly URL for example
Basic URL
en/article.aspx?PageID=13&Language=en-US&parID=5&Issue=17&cid=1&aid=173&wid=17
After Using URL Routing
en/article/en-US/13/13/17/1/173/this-is-the-title-of-the-article
My Routing Code in global.asax
routes.MapPageRoute("ArticleGeneral", "en/article/{Language}/{PageID}/{ParID}/{Issue}/{cid}/{aid}/{ArticleTitle}", "~/en/Article.aspx", false,
new RouteValueDictionary {
{ "Language", "en-US"},
{ "PageID", "0" },
{ "ParID", "0"},
{ "Issue", "0"},
{ "cid", "0"},
{ "aid", "0"},
{ "ArticleTitle", "event-not-found" }},
new RouteValueDictionary {
{"Language", "[a-z]{2}-[a-z]{2}"},
{ "PageID", "[0-9]{1,8}" },
{ "ParID", "[0-9]{1,8}" },
{ "Issue", "[0-9]{1,8}" },
{ "cid", "[0-9]{1,8}" },
{ "aid", "[0-9]{1,8}" }
});
I need to further work on this Routing to make it more like
en/article/CategoryName/WriterName/TitleOfArticle
I am not sure how i can replace ID with actual name using this routing pattern
I want to structure my pages similar to www.asp.net
http://www.asp.net/web-forms/overview
http://www.asp.net/web-forms/tutorials
http://www.asp.net/web-forms/videos
http://www.asp.net/web-forms/tutorials/aspnet-45/getting-started-with-aspnet-45-web-forms/url-routing
How can i structure my URL this way as they look clean. I am confused about this url as they are not passing any PageID's as part of URL are they using hidden fields or how they resolve these url.
My website is multilingual & I pass query-string like PageID & LanguageID for general page & for Articles i pass several query string as showing this example above.
I would appreciate answers regarding this with reference asp.net web-forms not MVC.
Pointer to a complete example would be great mostly example i have seen pass ID as part of Friend Url's.

Related

Embedding a pie chart in ASP.NET (Razor syntax)

I'm fairly new to ASP.NET (have been coding in WPF a long time, but new to ASP) and I'm trying to create a dashboard application that shows four pie charts. I've followed the instructions on this link, but I either get a chart on the entire page, or a broken image (as if the <img> source is not valid)
Can someone please explain to me how to achieve this behavior? I just want to display a simple pie chart on my page, that's it.
Thanks! (and sorry if this is a duplicated post, I've found other similar questions but none using the Razor syntax)
EDIT - Here's my code:
Index.cshtml:
<!DOCTYPE html>
<html>
<head>
<title>Chart Example</title>
</head>
<body>
<h1>Chart Example</h1>
<p>
The following chart is generated by the <em>ChartView.cshtml</em> file, but is shown
in this page.
</p>
<p><img src="ChartView.cshtml" /> </p>
</body>
</html>
My ChartView.cshtml: (located in the same folder as Index.cshtml)
#{
var myChart = new Chart(600, 400)
.AddTitle("Employees")
.AddSeries(chartType: "column",
xValue: new[] { "Peter", "Andrew", "Julie", "Mary", "Dave" },
yValues: new[] { "2", "6", "4", "5", "3" });
if (myChart != null)
{
myChart.Write();
}
}
This is what I get on the browser:
You can't return a .cshtml file (unless you fiddle dangerously with various internal settings - not recommended).
You should be pointing to an action.
The easiest way to test this is by opening the src directly in the browser - in this case "ChartView.cshtml" and you should get a 404.
You'll need to add an action to your controller that returns the view.
public HomeController : Controller
{
public ActionResult ChartView()
{
return View();
}
then
<img src='#Url.Action("ChartView", "Home")' alt="Chart"/>
You can test this by opening in the browser:
/Home/ChartView
(this assumes that ChartView.cshtml is in the folder "Views/Home")
As you're new to MVC, quick explanation. There a 'routeConfig.cs' which routes incoming urls to an action(method) on a controller(class). The controller loads the view (cshtml) and applies all the server-side code then returns the rendered html to the user.
You do not navigate directly to cshtml pages but navigate instead to actions via the url.
The default route config gives you urls such as:
http://[site]/[controller]/[action]
Note that [controller] does not include "Controller", so "HomeController" becomes "/Home/"
Also, by convention, if you don't specify a view name in 'View()' then it will look in your solution folder "/Views/[controller]/[action].cshml' (among other locations such as '/Views/Shared/[action].cshtml' (also configurable, but these are the defaults).
you easely can combine
http://morrisjs.github.io/morris.js/index.html
in mvc (razor syntax)
or you can use chart helper
http://www.asp.net/web-pages/overview/data/7-displaying-data-in-a-chart
change to:
#{
var myChart = new Chart(600, 400)
.AddTitle("Employees")
.AddSeries("SomeName",chartType: "Pie",
xValue: new[] { "Peter", "Andrew", "Julie", "Mary", "Dave" },
yValues: new[] { "2", "6", "4", "5", "3" });
if (myChart != null)
{
myChart.Write();
}
}
you have to lunch it in iis or some development server

REST Hypermedia URI Changes Based On Context in Web API (HATEOAS)

I am working on a new asp.net web api restful service and spent some time with some Pluralsight courses on the subject. One of the better ones dives deep into design and the implementation of hypermedia (HATEOAS).
I followed the implementation in the video as it was very straight forward and being new to mvc/web api it was really helpful to see it working end to end.
However as soon as I started to dig a bit deeper into my implementation, the use of a UrlHelper() to calculate the link to return began to fall apart.
In the code below, I have a simple Get() which returns a collection of a particular resources and then a Get(int id) which allows for the returning of a individual resource.
All of the results go through a ModelFactory which transforms my POCOs to return results and back again on post, patch and puts.
I was trying to do this in a more sophisticated way by allowing the ModelFactory to handle all of the intelligence of link creation since it is constructed using the Request object.
Now I know I could solve all of this by simply handling the link generation/inclusion right inside my methods and maybe that is the answer but I was curious how others are handling it.
My goal:
1) In result sets (i.e. collections of results returned by "Get()"), to include total item count, total page count, next and previous pages as necessary. I have implemented a custom json converter to drop empty links on the ground. For example, I do not print out "prevPage" when you are on the first page. This works today.
2) In individual results (i.e. result returned by "Get(id)"), to include links to self, include rel, method the link represents and whether or not it is templated. This works today.
What is broken:
As you will see in the output below, two things are "wrong". When you look at the "POST" link for a new individual item, the URL is correct. This is because I am stripping out the last portion of the URI (dropping the resource ID). When returning a result set however, the URI for a "POST" is now incorrect. This is because the route did not include the individual resource id since "Get()" was called, not "Get(id)".
Again, the implementation could be changed to produce different links depending on which method was hit, pulling them out of the factory and into controller but I would like to believe I am just missing something obvious.
Any pointers for this newbie to routing and Web API?
Controller Get()
[HttpGet]
public IHttpActionResult Get(int pageSize = 50, int page = 0)
{
if (pageSize == 0)
{
pageSize = 50;
}
var links = new List<LinkModel>();
var baseQuery = _deliverableService.Query().Select();
var totalCount = baseQuery.Count();
var totalPages = Math.Ceiling((double) totalCount / pageSize);
var helper = new UrlHelper(Request);
if (page > 0)
{
links.Add(TheModelFactory.CreateLink(helper.Link("Deliverables",
new
{
pageSize,
page = page - 1
}),
"prevPage"));
}
if (page < totalPages - 1)
{
links.Add(TheModelFactory.CreateLink(helper.Link("Deliverables",
new
{
pageSize,
page = page + 1
}),
"nextPage"));
}
var results = baseQuery
.Skip(page * pageSize)
.Take(pageSize)
.Select(p => TheModelFactory.Create(p))
.ToList();
return Ok(new DeliverableResultSet
{
TotalCount = totalCount,
TotalPages = totalPages,
Links = links,
Results = results
}
);
}
Controller Get(id)
[HttpGet]
public IHttpActionResult GetById(int id)
{
var entity = _deliverableService.Find(id);
if (entity == null)
{
return NotFound();
}
return Ok(TheModelFactory.Create(entity));
}
ModelFactory Create()
public DeliverableModel Create(Deliverable deliverable)
{
return new DeliverableModel
{
Links = new List<LinkModel>
{
CreateLink(_urlHelper.Link("deliverables",
new
{
id = deliverable.Id
}),
"self"),
CreateLink(_urlHelper.Link("deliverables",
new
{
id = deliverable.Id
}),
"update", "PUT"),
CreateLink(_urlHelper.Link("deliverables",
new
{
id = deliverable.Id
}),
"delete", "DELETE"),
CreateLink(GetParentUri() , "new", "POST")
},
Description = deliverable.Description,
Name = deliverable.Name,
Id = deliverable.Id
};
}
ModelFactory CreateLink()
public LinkModel CreateLink(string href, string rel, string method = "GET", bool isTemplated = false)
{
return new LinkModel
{
Href = href,
Rel = rel,
Method = method,
IsTemplated = isTemplated
};
}
Result of Get()
{
totalCount: 10,
totalPages: 4,
links: [{
href: "https://localhost/Test.API/api/deliverables?pageSize=2&page=1",
rel: "nextPage"
}],
results: [{
links: [{
href: "https://localhost/Test.API/api/deliverables/2",
rel: "self"
},
{
href: "https://localhost/Test.API/api/deliverables/2",
rel: "update",
method: "PUT"
},
{
href: "https://localhost/Test.API/api/deliverables/2",
rel: "delete",
method: "DELETE"
},
{
href: "https://localhost/Test.API/api/",
rel: "new",
method: "POST"
}],
name: "Deliverable1",
description: "",
id: 2
},
{
links: [{
href: "https://localhost/Test.API/api/deliverables/3",
rel: "self"
},
{
href: "https://localhost/Test.API/api/deliverables/3",
rel: "update",
method: "PUT"
},
{
href: "https://localhost/Test.API/api/deliverables/3",
rel: "delete",
method: "DELETE"
},
{
href: "https://localhost/Test.API/api/",
rel: "new",
method: "POST"
}],
name: "Deliverable2",
description: "",
id: 3
}]
}
Result of Get(id)
{
links: [{
href: "https://localhost/Test.API/api/deliverables/2",
rel: "self"
},
{
href: "https://localhost/Test.API/api/deliverables/2",
rel: "update",
method: "PUT"
},
{
href: "https://localhost/Test.API/api/deliverables/2",
rel: "delete",
method: "DELETE"
},
{
href: "https://localhost/Test.API/api/deliverables/",
rel: "new",
method: "POST"
}],
name: "Deliverable2",
description: "",
id: 2
}
Update 1
On Friday I found and began to implement the solution outlined here: http://benfoster.io/blog/generating-hypermedia-links-in-aspnet-web-api. Ben's solution is very well thought out and allows me to maintain my models (stored in a publicly available library for use in other .NET (i.e. RestSharp)) solutions and allows me to use AutoMapper instead of implementing my own ModelFactory. Where AutoMapper fell short was when I needed to work with contextual data (such as the Request). Since my HATEOAS implementation has been pulled out and into a MessageHandler, AutoMapper again becomes a viable option.
I extended Ben's solution (link below) and it has met every requirement I have placed on it. I believe that "enriching" the return result in the handlers with the required HATEOAS data is the way to go. The only time I need to set links directly outside of the handler is when I get into things like paging where only the controller has the necessary information to make the decision on what the links should look like. At that point, I simply add the link to the collection on my model which carries through to the handler where even more links might be added.
http://benfoster.io/blog/generating-hypermedia-links-in-aspnet-web-api
I have extended Ben's approach using ASP.NET Core. My approach uses a ResultFilter where the response is decorated with links. A link builder(enricher) is used for
every Model that supports Hypermedia links. Because there is not a official standard on how the links will be formatted, the Paypal's definitions are used.
Please check my blog Generating Hypermedia links for an ASP.NET Core Web API

ASP.NET 4 RouteDictionary does not take the default values

I have this route in ASP.NET 4
routes.MapPageRoute("Professions", // Route Name
"authors/profession/{Profession}/{letter}/page{pager}", // Url and Parameters
"~/Authors/Profession/Default.aspx", true,
new System.Web.Routing.RouteValueDictionary { { "letter", "a" }, { "pager", "1" } }); //
It works if I access the page like this
http://www.mysite.com/authors/profession/actor/a/page1
but this does not work http://www.mysite.com/authors/profession/actor (it should add automatically the letter 'a' and page 1, what I am doing wrong?
The problem you got here is the text "page" in the route which as after your {letter} token (which you are not providing in the example url).
For the routing engine to match that route, you would need both a "letter" and the text 'page' somewhere in the url. So your example /authors/profession/actor would not match whereas /authors/profession/actor/a/page would match and use the default value of 1 for the {pager} token.
I think the best way to approach this would be the following route setting:
routes.MapPageRoute("Professions", // Route Name
"authors/profession/{profession}/{letter}/{pager}", // Url and Parameters
"~/Authors/Profession/Default.aspx", true,
new System.Web.Routing.RouteValueDictionary { { "letter", "a" }, { "pager", "1" } });
Essentially, just take 'page' text out of the route so that when you have /authors/profession/actor it would go straight to the letter "a" and page "1". And a full url would be something like /authors/profession/actor/d/34.
Does that make sense? Your routing is perfectly valid, but you would always have to provide something for the {letter} token and have the text 'page' in there too.

ASP.NET Routing block asp.net menu control from showing horizntal sub menus

I am working on a project which is complete & while testing & found that ASP.NET Routing blocks menu from showing up on pages with friendly URL (ASP.Net 4.0, C#)
Routing Code
routes.MapPageRoute("ActivityRoute", "en/activity/{Language}/{EventID}/{PageID}/{EventTitle}", "~/en/Activity-Details.aspx", false,
new RouteValueDictionary {
{ "Language", "en-US"},
{ "EventID", "0" },
{ "PageID", "0"},
{ "EventTitle", "event-not-found" }},
new RouteValueDictionary {
{"Language", "[a-z]{2}-[a-z]{2}"},
{ "EventID", "[0-9]{1,8}" },
{ "PageID", "[0-9]{1,8}" }
});
Everything is working fine & managed to make Fancybox work with friendly URL but i am not able to figure out what is blocking sub menus from showing up on page with friendly URL.
I managed to resolve this issue as it was due to path
Resolved it, Problem was due to the path issue which was resolved

ASP.NET MVC 2 Routing with additional consistent parameters (not just controller & action)

Currently, I have URLs that look like this:
http://www.example.com/user/create
http://www.example.com/user/edit/1
But now, I have to support multiple organizations and their users. I need to have something like this:
http://www.example.com/org-name/user/create
http://www.example.com/org-name/user/edit/1
I was having trouble getting the routes to work just perfectly, so I had to add a token to the beginning of the organization name so that routing wouldn't confuse it with a controller/action pair. Not a huge deal but my URLs look like this now:
http://www.example.com/o/org-name/user/create
http://www.example.com/o/org-name/user/edit/1
That's fine. I can live with that.
Here's where I'm running into trouble:
When I generate URLs once I have an organization selected, it's not persisting the organization name. So when I'm here:
http://www.example.com/o/org-name
...and I use Url.Action("User", "Create") to generate a URL, it outputs:
/user/create
...rather than what I want:
/o/org-name/user/create
This is what my routes look like (in order):
routes.MapRouteLowercase(
"DefaultOrganization",
"{token}/{organization}/{controller}/{action}/{id}",
new { id = UrlParameter.Optional },
new { token = "o" }
);
routes.MapRouteLowercase(
"OrganizationDashboard",
"{token}/{organization}/{controller}",
new { controller = "Organization", action = "Dashboard" },
new { token = "o" }
);
routes.MapRouteLowercase(
"DefaultSansOrganization",
"{controller}/{action}/{id}",
new { controller = "Core", action="Dashboard", id = UrlParameter.Optional }
);
It's similar to this question ASP.NET MVC Custom Routing Long Custom Route not Clicking in my Head.
I have a feeling this is going to end up being obvious but it's Friday and it's not happening right now.
EDIT:
Womp's suggested worked but would this be the best way to automate this?
public static string ActionPrepend(this UrlHelper helper, string actionName, string controllerName)
{
string currentUrl = helper.RequestContext.RouteData.Values["url"] as string;
string actionUrl = string.Empty;
if (currentUrl != null)
{
Uri url = new Uri(currentUrl);
if (url.Segments.Length > 2 && url.Segments[1] == "o/")
actionUrl = string.Format("{0}{1}{2}{3}", url.Segments[0], url.Segments[1], url.Segments[2],
helper.Action(actionName, controllerName));
}
if(string.IsNullOrEmpty(actionUrl))
actionUrl = helper.Action(actionName, controllerName);
return actionUrl;
}
EDIT:
Fixed my routes to work rather than hacking it together. The final solution didn't need the stupid {token} in the URL. Maybe this'll help someone else:
routes.MapRouteLowercase(
"Organization",
"{organization}/{controller}/{action}/{id}",
new { controller = "Organization", action = "Dashboard", id = UrlParameter.Optional },
new { organization = #"^(?!User|Account|Report).*$" }
);
routes.MapRouteLowercase(
"Default",
"{controller}/{action}/{id}",
new { controller = "Core", action = "Dashboard", id = UrlParameter.Optional }
);
Url.Action uses route values to generate the actual URL's by querying the virtual path provider and attempting to match the most specific route. In the form that you are using, you are supplying values for the controller and the action, which is as deep as most simple websites go, hence the convenient form of the method. When Url.Action queries the routing system, it only has a "controller" and an "action" segment to match.
If you give the method the rest of the routing information it needs, it will properly match the route that you desire, and will return the correct URL. Try this:
Url.Action("User", "Create", new { token = "o", organization = "organization" })

Categories