I have a blogging application and users are able to create posts, I used Entity framework to create the model from the database. In Posts I have an entry for UrlSlug.
However when I check the details of a post:
Controller:
public ActionResult Details(int id = 0)
{
Post post = db.Posts.Find(id);
if (post == null)
{
return HttpNotFound();
}
return View(post);
}
It returns a url with id at the end: http://localhost:52202/Post/Details/1
I have tried returning post.UrlSlug (throws an error) aswell as changing my RouteConfig.cs file to use urlslug instead of id (which does display the urlslug, but it cant find the page because of the controller). How can I change this so urlslug is displayed instead of the Post Table Id?
RouteConfig.cs
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 }
);
}
View:
#for (int i = 0; i < Model.Count(); i += 3)
{
<div class="row">
#foreach (var item in Model.Skip(i).Take(3))
{
<div class="col-md-4 portfolio-item">
<a href="#Url.Action("Details", "Post", new { id = item.Id })">
<img class="img-responsive" src="http://placehold.it/700x400" alt="">
</a>
<h3>
#Html.DisplayFor(modelItem => item.Title)
</h3>
<p>#Html.DisplayFor(modelItem => item.ShortDescription)</p>
</div>
}
</div>
}
#Html.PagedListPager(Model, page => Url.Action("Index", new { page, pageSize = Model.PageSize }))
Edit
An issue I noticed is on this line:
public ActionResult Details(string urlslug)
{
Post post = db.Posts.Find(urlslug); //find only finds an eitity with a given primary key, how can I change this to find the string urlslug
An action method does not dictate the URL, it only matches a request. You need to print the slug:
#Url.Action("Details", "Post", new { slug = item.Slug })
Then add to the routing to support the URL structure you want:
routes.MapRoute(
name: "PostWithSlug",
url: "Post/Details/{slug}",
defaults: new { }
And alter the action method:
public ActionResult Details(string slug)
{
}
But besides that, you do want an ID in the URL. Otherwise you can't use the same post title twice.
You can either rename your argument in your controller to match the 'slug' name. So instead of
public ActionResult Details(int id = 0)
Use
public ActionResult Details(int urlslug = 0)
Alternative is to remove id from your route config like
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
Keep in mind, what ever you argument name is in your Action, that name is used as the GET variable in your url. So if the argument is int id, then your url must be like ?id=1.
An issue I noticed on the below line:
public ActionResult Details(string urlslug)
{
Post post = db.Posts.Find(urlslug); //find only finds an eitity with a given primary key, how can I change this to find the string urlslug
You can select data with the help of urlslug by using Where instead of Find
i.e. Post post = db.Posts.Where(x=> x.urlslug == urlslug).FirstOrDefault();
Related
I am working with MVC5 where I am unable to put dashes in the URL. What I want is to put a dash between the words.Kindly view my working code.
My Route Config is:
routes.Add(
new Route("{MyApple}/{page}",
new RouteValueDictionary(
new { controller = "MyApple", action = "Index", page = "" }),
new HyphenatedRouteHandler())
);
And My controller is :
public ActionResult Index(string page)
{
if (System.IO.File.Exists(Server.MapPath("/Views/MyApple/" + page + ".cshtml")))
{
string userId = "";
if (Session["AppId"] != null)
userId = Session["AppId"].ToString();
Session["ReturnUrl"] = Url.Action("EmptyTemplate");
IBaseViewModel model = new BaseViewModel();
return View(page, model);
}
}
This is my controller where the parameters go to page.
Example: devpage = aspdotnet then it will render the view = aspdotnet.html
Please help with this edited content.
I want to dash to the URL with lowercases.
Example: Suppose I have perfectsolution.html page then i
localhost/case-study/perfect-solution
(Don't mind my English).Help will be appreciated. Thanks
Note: in the index page, I` am using "perfect solution" to pass to the view but I want in URL, perfect-solution.
I want this type URL /case-studies/san-Diego-veg-festival ,
casestudies is controller and san-diego-veg-festival is parameter.
As per your comments , I made a guess that you are trying to pass parameter value in CaseStudies controller's Index method.
Two types of Routing to choose from, you can choose anyone as required:
1. Convention-based Routing
In Your RouteConfig.cs file make these changes:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "CaseStudiesRoute",
url: "case-studies/{devPage}",
defaults: new { controller = "CaseStudies", action = "Index", devPage = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Controller:
public class CaseStudiesController : Controller
{
public ActionResult Index(string devPage)
{
return View();
}
}
Now using your browser give a GET request to URL , /case-studies/san-Diego-veg-festival , It should work with Convention-based Routing.
2. Attribute Routing
In Your RouteConfig.cs enable Attribute Routing , by adding routes.MapMvcAttributeRoutes(); , Now RouteConfig.cs looks like:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Controller:
public class CaseStudiesController : Controller
{
[Route("case-studies/{devPage?}")]
public ActionResult Index(string devPage)
{
return View();
}
}
Now using your browser give a GET request to URL , /case-studies/san-Diego-veg-festival , It should work with attribute routing.
References
1.
2.
Try using Routing Attribute functionality
[Route("study/perfect-solution")]
eg:
[Route("study/perfect-solution")]
public ActionResult PerfectSolution()
{
// code here
}
If you want to pass parameters it need to be specified
Eg:
[Route("study/perfect-solution/{parameter}")]
public ActionResult PerfectSolution(string parameter)
{
// code here
}
The default url of the application is
http://servername/Root/ITProjects
There is a menu item with 'Team' on the default page which should redirect to another controller with index which will display a dropdownlist at the following url
http://servername/Root/ITProjects/Team
It's working fine so far.
However when the user selects an item in the dropdownlist, it should go to the url below
http://servername/Root/ITProjects/Team/Index?Id=7
But it's directing to
http://servername/Team/Index?Id=7
and throwing the 404 error. It is missing the folder path 'Root/ITProjects' after the servername. It's working fine on localhost where there is no folder path but failing on deployment to test or prod servers
[AuthorizeAD(Groups = "APP_xxxx_Users")]
public class TeamController : Controller
{
public int pageSize = 5;
private IProjectService _projectService;
public TeamController(IProjectService projectService)
{
this._projectService = projectService;
}
public ActionResult Index(int? Id, int? page)
{
int pageNumber = (page ?? 1);
var viewModel = new TeamViewModel();
if (Id != null)
{
viewModel.SelectedMember = (int)Id;
viewModel.Tasks = this._projectService.GetTasksByStaff(viewModel.SelectedMember).ToPagedList(pageNumber, pageSize);
}
return View(viewModel);
}
[HttpPost, ActionName("Index")]
public ActionResult IndexPost(int Id, int? page)
{
int pageNumber = (page ?? 1);
var viewModel = new TeamViewModel();
viewModel.SelectedMember = Id;
if (ModelState.IsValid)
{
viewModel.Tasks = this._projectService.GetTasksByStaff(viewModel.SelectedMember).ToPagedList(pageNumber, pageSize);
}
return View(viewModel);
}
}
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 }
);
}
As part of Index view when dropdownlist item is selected
#Html.DropDownListFor(m => m.SelectedMember, new SelectList(Model.StaffList, "UniqueId", "Initials", Model.SelectedMember), new { #class = "btn btn-default btn-color", onchange = #"form.action='/Team/Index?Id=' +this.value;form.submit();" })
Your MVC application is in a sub-directory of another application, so IIS considers the root of the parent application instead of the root of your MVC application.
In IIS, you need to convert the virtual directory to an Application: choose your virtual directory and "Convert to Application." If you are unable to do that, then you need to modify your routes in routes.config to take the virtual directory into account:
routes.MapRoute(
"Default", // Route name
"Root/ITProjects/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
your default route for controllers looks like this :
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
You can add /Root/ITProjects/ before the URL to change the default controller path
I have statically mapped all controllers except one ... a LookupController that I would like to operate off root.
Here are my routes:
routes.MapRoute(
name: "Root",
url: "",
defaults: new {controller = "Home", action = "Index"}
);
routes.MapRoute(
name: "Home",
url: "home/{action}/{id}");
routes.MapRoute(
name: "User",
url: "user/{action}/{id}");
routes.MapRoute(
name: "Company",
url: "company/{action}/{id}");
routes.MapRoute(
name: "Recruiter",
url: "recruiter/{action}/{id}");
routes.MapRoute(
name: "Lookup",
url: "{company}/{recruiter}",
defaults: new { controller = "Lookup", action = "Index" }
);
Everything works fine EXCEPT the last entry for Lookup.
This breaks everything, including the other routes.
Basically, I want my URLs to be:
/Home
/User
/Company
/Recruiter
/{company}/{recruiter}
Any idea how I can accomplish this?
Thank you very much in advanced!
EDIT: Adding LookupController by request:
public class LookupController : Controller
{
private RecruiterSightContext db = new RecruiterSightContext();
// GET: Lookup
public ActionResult Index(string company, string recruiter)
{
if (string.IsNullOrEmpty(company) && string.IsNullOrEmpty(recruiter))
{
TempData["errorMessage"] = "No Company or Recruiter specified in URL";
return RedirectToAction("Index", "Home");
}
// Company lookup
if (string.IsNullOrEmpty(recruiter))
{
Company foundCompany = db.Companies.FirstOrDefault(c => company.ToLower().Equals(c.Slug.ToLower()));
if (foundCompany == null)
{
return RedirectToAction("Index", "Home");
}
return RedirectToAction("Display", "Company", new {companyId = foundCompany.CompanyId});
}
Recruiter foundRecruiter = db.Recruiters.FirstOrDefault(r => r.Slug.ToLower().Equals(recruiter.ToLower()));
if (foundRecruiter == null)
{
return RedirectToAction("Index", "Home");
}
return RedirectToAction("RecruiterView", "Recruiter", new {recruiterId = foundRecruiter.RecruiterId});
}
}
If your Lookup folder exists:
try to change:
from:
routes.MapRoute(
name: "Lookup",
url: "{company}/{recruiter}",
defaults: new { controller = "Lookup", action = "Index" }
);
to:
routes.MapRoute(
name: "Lookup",
url: "company/recruiter",
defaults: new { controller = "Lookup", action = "Index" }
);
then in your Browser
Add like this:
company/recruiter
If your Lookup is different of index then i think you need to create another function that calling your Lookup like:
routes.MapRoute(
name: "Lookup",
url: "company/recruiter",
defaults: new { controller = "Lookup", action = "LookupWithParameter" }
);
then in your Controller:
public ActionResult LookupWithParameter(String Company, string recruiter)
And don't forget to create another .cshtml file to redirect the page of your Lookup which is located at your Lookup folder
Here's the scenario:
Assuming that you are currently working at your Index.cshtml page which in your Home Controller then you want to redirect into Lookup page.
Index(Home) .cshtml file:
<div id="content">
#{
if (IsPost) {
<!--this option if you don't need to change another page-->
string company = Request["Company"];
string recruiter = Request["Recruiter"];
<p>You entered: <br />
Company Name: #company <br />
Recruiter Name: #recruiter</p>
}
else
{
<form method="post" action="company/recruiter">
Company Name:<br />
<input type="text" name="Company" value="" id="idcompany" /><br />
Contact Name:<br />
<input type="text" name="Recruiter" value=""id="idrecruiter" /><br /><br />
<input type="submit" value="Submit" class="submit" />
</form>
}
}
</div>
LookupController:
public class LookupController : Controller
{
//
// GET: /Lookup/
public ActionResult MyLookup(string Company, string Recruiter)
{
// this is how to pass the value of parameter company and recruiter input from index.
//Your Action here and you can use to Debug while runnning notice that the company and the recruiter has a content from index home input
return View();
}
}
RouteConfig.cs
routes.MapRoute(
name: "Lookup",
url: "company/recruiter",
defaults: new { controller = "Lookup", action = "MyLookup" }
);
Make sure that you have a Lookup folder that contains MyLookup.cshtml
This question is really similar to my last one but this has to do with passing multiple parameters. My controller has two methods with the same name but one gets passed a nullable int to determine which action to take on post (I don't know if that's a good design decision). Here's the code:
public ActionResult EventDetails(int? eventID)
{
EventDetailsViewModel model = new EventDetailsViewModel();
if (eventID != null)
{
model.afterPost = false;
model.currentAttendance = getCountAttending(eventID);
model.currentUser = getCurrentUserProfile(User.Identity.Name);
model.eventDetails = getEventDetails(eventID);
model.eventRestrictions = getEventRestrictions(eventID);
model.usersAttending = getUsersAttending(eventID);
return View(model);
}
else
{
return View();
}
}
[HttpPost]
public ActionResult EventDetails(int? eventID, int? action)
{
EventDetailsViewModel model = new EventDetailsViewModel();
if (eventID != null)
{
model.afterPost = true;
model.currentAttendance = getCountAttending(eventID);
model.currentUser = getCurrentUserProfile(User.Identity.Name);
model.eventDetails = getEventDetails(eventID);
model.eventDetails["ActionID"] = action.ToString();
model.eventRestrictions = getEventRestrictions(eventID);
model.usersAttending = getUsersAttending(eventID);
return View(model);
}
else
{
return View();
}
}
the view is huge but I'll post the relevant piece:
#if(User.Identity.IsAuthenticated)
{
if (!Model.eventDetails["Event_Owned"].Equals("true"))
{
<div class="joinEventButton">
#if(Model.eventDetails["Event_Current"].Equals("true"))
{
<form method="post" action="#Url.Action("EventDetails", "Event", new{eventID = Int32.Parse(Model.eventDetails["EventID"]), action = Int32.Parse(Model.eventDetails["CODE_RequestInvitation"])})">
<input type="submit" value="Request Invitation" class="submitButton submitButton-green"/>
</form>
}
else
{
<button class="disabledButton disabledButton-grey">Request Invitation</button>
}
</div>
}
and just for good measure, my routes:
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 }
);
}
So the idea is that based on authentication and whether they own a specific event they are looking at, they will have different buttons show on the screen. This certain one is for when they want to join an event they can push a button to request an invitation. The controller knows this because of the int value being passed back as CODE_RequestInvitation. The action being returned is always null. I can't figure out what's wrong.
You problem is the use of the parameter name action (which is conflicting with the action attribute). Change it to another name and it will work. Inspect he html of the <form> element before and after the change.
You code for generating the form is awful and the use of Int32.Parse() is pointless.
#using (Html.BeginForm("EventDetails", "Event", { eventID = Model.eventDetails["EventID"], actionID = Model.eventDetails["CODE_RequestInvitation"] }, FormMethod.Post))
{
<input type="submit" value="Request Invitation" class="submitButton submitButton-green"/>
}
and post back to
[HttpPost]
public ActionResult EventDetails(int? eventID, int? actionID)
I'm looking to have a SEO friendly route for my blog using MVC 4. I have looked over several stackoverflow articles and tried to utilize the advise but, I still cant get it to work properly.
What I am try to do is when someone clicks on one of the blog post, the route displays:
blog/{blogCategory}/{blogTitle}, for example "blog/cleaning/how-to-remove-stains". I am missing something important. I have included the controller, view and route. I have two questions:
Why when I pass the blogTitle ("The rain in spain") to the controller it comes in with hyphens and spaces? I want to be able to take the title and run it through a function to make it URL friendly and put it back out to be displayed in the route.
Why does the route only partially work? Instead of displaying blog/{blogCategory}/{blogTitle} it displays blog/{blogCategory}?querystrings see example below: blog/uncategorized/the%20-rain%20in%20-spain%20-falls%20-mainly%20on%20the%20-plains?id=2. (Note: uncategorized is one of the categories)
ROUTE CONFIG:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.Canonicalize().NoWww().Pattern("([a-z0-9])([A-Z])", "$1-$2").Lowercase().NoTrailingSlash();
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Blog",
"Blog/{blogCategory}/{blogTitle}",
new { controller = "Blog", action = "Details" }
).RouteHandler = new Spotless_Interiors.Models._GlobalClasses.HyphenatedRouteHandler();
// Original route map
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
).RouteHandler = new Spotless_Interiors.Models._GlobalClasses.HyphenatedRouteHandler();
}
}
VIEW:
#model Spotless_Interiors.Models.MultiModels
#{
ViewBag.Title = "Blog";
ViewBag.Separator = " | ";
ViewBag.Path = Request.Path;
}
#section menuLeft {
#Html.Partial("_BlogMenu")
}
#Html.Partial("_TitleBlockPartial", Model.Company)
#if (User.Identity.IsAuthenticated) {
<div class="blog-links">
#Html.ActionLink("Create New", "Create", "Blog", new { DT_Stamp = #DateTime.Now })
#ViewBag.Separator
<a href="#Url.Action("Meta-Tag-Editor", "Dashboard", new { pageName = (string)ViewBag.pageName, path = (string)ViewBag.Path })" >MetaTag</a>
<hr />
</div>
}
<div id="blog">
#foreach (var item in Model.PagedPosts)
{
<h1>#Html.ActionLink(item.Title, "Details", new { id = item.PostID, blogCategory = item.Category.CategoryName, blogTitle = item.Title }, new { #class ="blog-title" }) </h1>
<div class="full-width">
<div class="blog-category">
<h2>#Html.DisplayFor(modelItem => item.Category.CategoryName) | #Html.DisplayFor(modelItem => item.DT_Stamp)</h2>
</div>
<div class="blog-links">
#Html.ActionLink("Read More >>", "Details", new { id = item.PostID, blogCategory = item.Category.CategoryName, blogTitle = item.Title }, new { #class ="blog-links" })
</div>
</div>
<div class="full-width">
</div>
#Html.Raw(Server.HtmlDecode(item.TruncatedBody.Replace("<img ", "<img style = 'width:100px' ")))
<div class="ribbon" style="clear:both"></div>
}
</div>
CONTROLLER:
// GET: /Blog/Details
public ActionResult Details(int id = 0, string blogCategory = "", string blogTitle = "")
{
pageName = "Blog Details";
ViewBag.pageName = pageName;
var multiModels = _globalClasses.Standard(ViewBag.pageName, Request.Path);
///multiModels.Post = db.Posts.First(i => i.PostID == id);
multiModels.Post = db.Posts.First(i => i.PostID == id);
multiModels.PostComments = db.PostComments.Where(i => i.PostPostID == id).ToList();
blogTitle = _globalClasses.UrlFriendly(blogTitle);
if (multiModels.Post == null)
{
return HttpNotFound();
}
return View(multiModels);
}
Perhaps your custom extension methods Canonicalize(?) and custom route handlers mess things up.
Concerning ASP.NET MVC this is enough:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(name:"Blog", url:"Blog/{blogCategory}/{blogTitle}",
defaults:new {controller = "Blog", action = "Details"});
routes.MapRoute(name:"Default", url:"{controller}/{action}/{id}",
defaults:new {controller = "Home", action = "Index", id = UrlParameter.Optional});
}
And controller:
public class BlogController : Controller
{
public ActionResult Details(string blogCategory, string blogTitle)
{
return Content(string.Format("category: {0}, title: {1}", blogCategory, blogTitle));
}
}
For path /blog/my%20category/my%20title, you get correct response "category: my category, title: my title".