In my bus service MVC project I need to be able to display a bus route stops only when i go to the route table. I have this ActionLink sending the querystring and the page to the route table: #Html.ActionLink("Route Stops", "index", "snRouteStops", new { id=item.busRouteCode}, null)
But when I get to my bus route stops controller I need it to throw me back to the route lists if there isn't one selected. This part works fine but when I click a route to view the stops nothing happens. Here is my code in the actionResult of the busRouteStops controller:
public ActionResult Index()
{
string busRouteCode = "";
if (string.IsNullOrWhiteSpace(Request.QueryString["busRouteCode"]))
{
if (Request.Cookies["busRouteCode"] == null)
{
return RedirectToAction("index", "snBusRoutes");
}
else
{
busRouteCode = Request.Cookies["busRouteCode"].ToString();
}
}
else
{
busRouteCode = Request.QueryString["busRouteCode"].ToString();
}
var routeStops = db.routeStops.Include(r => r.busRoute).Where(r => r.busRouteCode == busRouteCode).Include(r => r.busStop);
return View(routeStops.ToList());
}
}
Your main issue is that you are looking for a query string with the name busRouteCode, but your action link is not setup to provide a query string with that name. Your action link -
#Html.ActionLink("Route Stops", "index", "snRouteStops", new { id=item.busRouteCode}, null)
is configured to send the busRouteCode as a parameter with the name id. That means that the URL will look like this:
/snRouteStops/Index?id=myBusRouteCode
Thus, there is no query string with the name busRouteCode
You can do a few things to clean this up.
Standard routing in MVC is configured to use /Controller/Action/id. You can update your index action to be aware that an Id is coming by updating the action method.
//appears id is a string based on your code
public ActionResult Index(string id)
{
string busRouteCode = "";
if(string.IsNullOrEmpty(id))
{
if (Request.Cookies["busRouteCode"] == null)
{
return RedirectToAction("index", "snBusRoutes");
}
else
{
busRouteCode = Request.Cookies["busRouteCode"].ToString();
}
}
else
{
busRouteCode = id;
}
var routeStops = db.routeStops.Include(r => r.busRoute).Where(r => r.busRouteCode == busRouteCode).Include(r => r.busStop);
return View(routeStops.ToList());
}
}
Change your action link to send the busRouteCode as a parameter named busRouteCode.
#Html.ActionLink("Route Stops", "index", "snRouteStops", new { busRouteCode=item.busRouteCode}, null)
This will generate a link that looks like:
/snRouteStops/Index?busRouteCode=myBusRouteCodeValue
However you proceed, you should not really ever have to use Request.QueryString[] in your MVC controller actions. By configuring your action method to be looking for those query string values(like I did in example one), you can now get strongly typed parameters in your action methods as opposed to the string values in the QueryString[] dictionary. This is especially helpful when expecting an integer as a query string value for instance. The model binder will take care of ensuring that query string value is actually an integer and not a string.
This code {id=item.busRouteCode} generates query like that:
http://snRouteStops/Index?id=somedata
where somedata = item.busRouteCode.
Try to look for id in query string or just add id to action params, like that:
public ActionResult Index(string id)
Related
I am building a basic Car Rental Application. The user can view the cars and click the Rent button. After clicking it, I need to return a new View which contains a form, that the user has to complete in order to finish the order. I am having problems passing the Car data as well as the Customer data between the controllers in order to complete the Rent.
On the main page, I have a Rent link under every car. Here is the code:
<div class="col-md-12">
<p>#Html.ActionLink("Rent", "Rent" , new { Id = car.Id})</p>
</div>
Rent method from HomeController
public ActionResult Rent(string id)
{
return RedirectToAction("Create", "Rents");
}
Create method from RentsController
[HttpPost]
public ActionResult Create(string carId, Rent rent)
{
if (!ModelState.IsValid)
return View();
var carToRent = context.Cars.SingleOrDefault(c => c.Id == carId);
if (carToRent == null)
return Content($"Car not found!");
rent.Car = carToRent;
var customer = context.Customers.SingleOrDefault(c => c.UserId == User.Identity.Name);
if (customer == null)
return Content($"Customer not found!");
rent.Customer = customer;
context.Rents.Add(rent);
context.SaveChanges();
return RedirectToAction("Index");
}
I am getting an HTTP 404 Error every time I try to access Rents/Create.
You can simplify what you're attempting to do. Main points to note are the following:
You don't need to link to the Rent action if all it does is
redirect to the Create action- just link to the Create action
directly. There is another overload of ActionLink that will let you specify
the controller (see below).
From what you've posted it doesn't look like the Create action
needs to take in a parameter for Rent rent- this can be created
inside the Create action and simplify the data that you need to
pass from view to controller.
Please see my comments in code for further explanantion:
View:
//call the Create action on the RentsController directly from the view
<div class="col-md-12">
<p>#Html.ActionLink("Rent", "Create", "Rents" , new { Id = car.Id }, null)</p>
</div>
Controller:
//modify signature to remove passing a Rent object it
//you can create this object inside of this method
//and do not need to pass one in so remove it from the method signature
[HttpPost]
public ActionResult Create(string carId)
{
if (!ModelState.IsValid)
return View();
var carToRent = context.Cars.SingleOrDefault(c => c.Id == carId);
if (carToRent == null)
return Content($"Car not found!");
var rent = new Rent(); //this line has been added since the method signature was changed
rent.Car = carToRent;
var customer = context.Customers.SingleOrDefault(c => c.UserId == User.Identity.Name);
if (customer == null)
return Content($"Customer not found!");
rent.Customer = customer;
context.Rents.Add(rent);
context.SaveChanges();
return RedirectToAction("Index");
}
and finally you can remove the following:
//delete this action entirely, if youre doing nothing other than redirecting
//to an action then just link directly to the action you want
//notice the ActionLink in the view is modified to hit the Create action directly
public ActionResult Rent(string id)
{
return RedirectToAction("Create", "Rents");
}
As you can see below you can pass parameters in RedirectToAction() method.
RedirectToAction(String, String, RouteValueDictionary)
Redirects to the specified action using the action name, controller name, and route values. Try to redirect Create action with the carId and Rent object.
I dont know using multiple post object, but you can post one post object like that
public class MyPostObject
{
public string carId { get; set; }
public Rent rent{ get; set; }
}
and post it like that
[HttpPost]
public ActionResult Create(MyPostObject myPostObject)
{
string carId=myPostObject.carId;
Rent rent = myPostObject.rent;
....
}
UPDATE : Or you can use multiple post object with Ajax
$("#btnSave").on('click', function () {
var url = '#Url.Action("Create", "Rent")';
//Rent class properties
var data=
{
Brand: 'Renault',
Model: 'Megan',
};
$.ajax({
url:url,
type:"POST",
data:{
carId:'12',
rent:data
},
datatype:'json',
ContentType:'application/json;utf-8'
}).done(function(resp){
alert('Success ' +resp);
}).error(function(err){
alert("Error " + err.status);
});
});
As mentioned in the comments, you will have to pass the required parameters into the redirect statement.
public ActionResult Rent(string id)
{
Rent rentItem = new Rent();
return RedirectToAction("Create", "Rents", new { carId = id, rent = rentItem});
}
You either have not passed the parameters or you are missing the below method if you are looking to return a view with your redirect
public ActionResult Create()
{
return View();
}
I am trying to make a MVC view accessible from either directly going to that View from a menu or by clicking on a link that'll take me to that same view but with a parameter and with that particular links information instead of seeing a general page if I went straight to it.
public ActionResult Roles(Guid applicationId)
{
if (applicationId == Guid.Empty)
{
return View();
}
var application = new ApplicationStore().ReadForId(applicationId);
return View(application);
}
I know for optional parameters you I'd do something like Guid? in the parameters but visual studios doesn't like that and I can't do Guid application = null either. Any Ideas?
As you already mentioned, make the parameter optional.
public ActionResult Roles(Guid? id) {
if (id == null || id.Value == Guid.Empty) {
return View();
}
var application = new ApplicationStore().ReadForId(id.Value);
return View(application);
}
This also assumes the default convention-based route
"{controller}/{action}/{id}"
Where the id is optional in the route template.
id = UrlParameter.Optional
For example
routes.MapRoute(
name: "SomeName",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Index", id = UrlParameter.Optional }
);
You could potentially just change the Guid parameter to string.
Guid.Empty = 00000000-0000-0000-0000-000000000000 which may cause issues when trying to pass in a null value.
if you switch it to something like this (but still use a Guid):
public ActionResult Roles(string applicationId)
{
if (string.IsNullOrEmpty(applicationId))
{
return View();
}
var application = new ApplicationStore().ReadForId(applicationId);
return View(application);
}
it may side-step the errors you're encountering.
I'm developing on a school project where it has gone great so far but the problem has finally arrived.
I have this method in my homecontroller:
[HttpPost]
public ActionResult GetSeatsInShow(int showId)
{
...
return View(realSeatList);
}
In my view I use razor to parse a parameter to the controller and the controller should return the result in an address like this:
http://localhost:1499/Home/GetSeatsInShow/236
That is what it does if I do it on my other controller method called Shows on the following URL:
http://localhost:1499/Home/Shows/1
But on the GetSeatsInShow method will I need to place ?showId= like to snippet right under:
http://localhost:1499/Home/GetSeatsInShow/?showId=236
My razor ActionLink looks like this:
#Html.ActionLink("Vælg", "GetSeatsInShow", new { id = item.Id })
Can't seem to find the problem aftersome the Show method works fine with the same results as the one that doesn't work.
You have a few options.
You're currently passing the parameter as id, not showId, whereas your controller expects a parameter named showId. As a result, by updating your anonymous type you can pass the correct parameter name.
#Html.ActionLink("Vælg", "GetSeatsInShow", new { showId = item.Id })
Alternatively, you can simply pass "id" in your querystring and allow the default MVC binding to work it's magic if you update your controller:
#Html.ActionLink("Vælg", "GetSeatsInShow", new { id = item.Id })
[HttpPost]
public ActionResult GetSeatsInShow(int id)
{
...
return View(realSeatList);
}
You can try this
#Html.ActionLink("Vælg", "GetSeatsInShow", new { id = item.Id, showId = item.Id })
you are taking an input of showId in GetSeatsInShow and in the razor you are passing in id
[HttpPost]
public ActionResult GetSeatsInShow(int id)
{
...
return View(realSeatList);
}
I want to create a url like that below:
www.mywebapp.com/Users/Profile/john
I have the UsersController controller and the Profile action, which returns a ViewResult to the Profile page.
I've created a route to manage it:
routes.MapRoute(
name: "ProfileRoute",
url: "Users/Profile/{username}",
defaults: new { controller = "Users", action = "Profile", username = UrlParameter.Optional }
);
The first question is: If i change {username} by {id}, it works. When I put {username} like parameter, the action gets NULL in the parameter. Why this?
Here's my action called Profile:
[HttpGet]
public ActionResult Profile(string id) {
if (UsersRepository.GetUserByUsername(id) == null) {
return PartialView("~/Views/Partials/_UsernameNotFound.cshtml", id);
}
return View(id);
}
I've added a View page to show the user profile. However, when the method ends its execution, I got another error:
The view 'john' or its master was not found or no view engine supports the searched locations.
The following locations were searched:
~/Views/Users/john.aspx
~/Views/Users/john.ascx
...
The second question is: The page I have to show is Profile, not a page with the username's name. Why is it happening?
You are getting this error because you are passing a string (id) to the View function, this overload searches for a view with the name passed in the string (in this case the username).
if you are simply trying to pass the username directly to the view you can use something like a ViewBag, so your code should look like this:
public ActionResult Profile(string id) {
if (UsersRepository.GetUserByUsername(id) == null) {
return PartialView("~/Views/Partials/_UsernameNotFound.cshtml", id);
}
ViewBag.Username=id;
return View();
}
I might be reading this incorrectly, but if you change the name of the required parameter from
id to username, it shouldn't return null
[HttpGet]
public ActionResult Profile(string username) {
if (UsersRepository.GetUserByUsername(username) == null) {
return PartialView("~/Views/Partials/_UsernameNotFound.cshtml", id);
}
return View(username);
}
I have a View called Browse.chtml, where the user can enter a search term, or leave the search term blank. When entering the search term, I want to direct the page to http://localhost:62019/Gallery/Browse/{Searchterm} and when nothing is entered, I want to direct the browser to http://localhost:62019/Gallery/Browse/Start/Here.
When I try this, I get the error:
The current request for action 'Browse' on controller type 'GalleryController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Browse(System.String) on type AutoApp_MVC.Controllers.GalleryController
System.Web.Mvc.ActionResult Browse(Int32, System.String) on type AutoApp_MVC.Controllers.GalleryController
Everything I'm doing with MVC is for the first time. I'm not sure what else to try at this point.
public ActionResult Browse(string id)
{
var summaries = /* search using id as search term */
return View(summaries);
}
public ActionResult Browse(string name1, string name2)
{
var summaries = /* default list when nothing entered */
return View(summaries);
}
I also have this in Global.asax.cs:
routes.MapRoute(
"StartBrowse",
"Gallery/Browse/{s1}/{s2}",
new
{
controller = "Gallery",
action = "Browse",
s1 = UrlParameter.Optional,
s2 = UrlParameter.Optional
});
routes.MapRoute(
"ActualBrowse",
"Gallery/Browse/{searchterm}",
new
{
controller = "Gallery",
action = "Browse",
searchterm=UrlParameter.Optional
});
You can only have a maximum of 2 action methods with the same name on a controller, and in order to do that, 1 must be [HttpPost], and the other must be [HttpGet].
Since both of your methods are GET, you should either rename one of the action methods or move it to a different controller.
Though your 2 Browse methods are valid C# overloads, the MVC action method selector can't figure out which method to invoke. It will try to match a route to the method (or vice versa), and this algoritm is not strongly-typed.
You can accomplish what you want using custom routes pointing to different action methods:
... in Global.asax
routes.MapRoute( // this route must be declared first, before the one below it
"StartBrowse",
"Gallery/Browse/Start/Here",
new
{
controller = "Gallery",
action = "StartBrowse",
});
routes.MapRoute(
"ActualBrowse",
"Gallery/Browse/{searchterm}",
new
{
controller = "Gallery",
action = "Browse",
searchterm = UrlParameter.Optional
});
... and in the controller...
public ActionResult Browse(string id)
{
var summaries = /* search using id as search term */
return View(summaries);
}
public ActionResult StartBrowse()
{
var summaries = /* default list when nothing entered */
return View(summaries);
}
You might also be able to keep the action methods named the same in the controller, by applying an [ActionName] attribute to one to distinguish it. Using the same Global.asax as above, your controller would then look like this:
public ActionResult Browse(string id)
{
var summaries = /* search using id as search term */
return View(summaries);
}
[ActionName("StartBrowse")]
public ActionResult Browse()
{
var summaries = /* default list when nothing entered */
return View(summaries);
}
I don't know when the question was asked this solution was available but you can use:
Request.QueryString["key"]
So this should work fine for your problem:
[HttpGet]
public ActionResult Browse()
{
if( Request.QueryString["id"] != null )
var summaries = /* search using id as search term */
else /*assuming you don't have any more option*/
var summaries = /* default list when nothing entered */
return View(summaries);
}
Add following code in RouteConfig.cs before Default route
routes.MapMvcAttributeRoutes();
And add route attributes in the controller like:
[Route("Cars/deteals/{id:int}")]
public ContentResult deteals(int id)
{
return Content("<b>Cars ID Is " + id + "</b>");
}
[Route("Cars/deteals/{name}")]
public ContentResult deteals(string name)
{
return Content("<b>Car name Is " + name + "</b>");
}
I think the point being made is that you don't need to implicitly test for querystring parameters using the request class.
MVC does the mapping for you (unless you have made severe changes in your MVC routes).
Thus an actionlink path of
/umbraco/Surface/LoginSurface/Logout?DestinationUrl=/home/
would automatically be available to your (surface) controller with the parameter defined:
public ActionResult Logout(string DestinationUrl)
MVC does the work.