I have been trying to understand an online tutorial and I am stumped.
Can someone please tell me where the text "Hello" is sent to? Is the message sent directly to the browser without being placed on a page?
public class GoHomeController : Controller
{
public string Index()
{
return "Hello";
}
}
How's this? Your controller action needs to have a return type of ActionResult, there are many subclasses of this class that allow for various types of responses however you can always influence with brute force if you like. For example"
public ActionResult Index()
{
Response.Write("hello world");
return null;
}
The above code writes to the Response stream directly, in my example I return a null. This indicates no ActionResult is needed to be performed by the MVC system, typically this is where the View is specified, the View will be read, parsed and written to the Response stream as well.
But typical controller actions do have return values, for example here is how I could return JSON, remember the View is just an abstraction to allow you to control what is written to the Response stream.
public ActionResult Index()
{
return Json( new { Message="Hello world"});
}
And then there is the typical ActionResult that directs the output to a .cshtml file:
public ActionResult Index()
{
return View();
}
This will write to the Response stream using the Index.cshtml file tied to this controller namespace or I could specify the name of the .cshtml:
public ActionResult Index()
{
return View("HelloWorld"); //<-- looks for HelloWorld.cshtml
}
Related
I understand ActionResult is the base class for RedirectResult class so essentially functionality for the code below is the same.
Is there an advantage to explicitly specify the appropriate result class as a return type as opposed to the base class?
public ActionResult Index()
{
return Redirect("Home/Contact");
}
public RedirectResult Index()
{
return Redirect("Home/Contact");
}
if you need redirect quickly to another action without lost any data and this action is in the same controller , call the action as a method:
public IActionResult Index()
{
return Contact();
}
public IActionResult Contact()
{
....
}
which one from another redirections methods is better is an opinion-based question and is not allowed by SO policy
Could any body explain, when to use
TempData
ViewBag
ViewData
I have a requirement, where I need to set a value in a controller one, that controller will redirect to Controller Two and Controller Two will render the View.
I have tried to use ViewBag, the value gets lost by the time I reach Controller Two.
Can I know when to use and advantages or disadvantages?
Thanks
1)TempData
Allows you to store data that will survive for a redirect. Internally it uses the Session as backing store, after the redirect is made the data is automatically evicted. The pattern is the following:
public ActionResult Foo()
{
// store something into the tempdata that will be available during a single redirect
TempData["foo"] = "bar";
// you should always redirect if you store something into TempData to
// a controller action that will consume this data
return RedirectToAction("bar");
}
public ActionResult Bar()
{
var foo = TempData["foo"];
...
}
2)ViewBag, ViewData
Allows you to store data in a controller action that will be used in the corresponding view. This assumes that the action returns a view and doesn't redirect. Lives only during the current request.
The pattern is the following:
public ActionResult Foo()
{
ViewBag.Foo = "bar";
return View();
}
and in the view:
#ViewBag.Foo
or with ViewData:
public ActionResult Foo()
{
ViewData["Foo"] = "bar";
return View();
}
and in the view:
#ViewData["Foo"]
ViewBag is just a dynamic wrapper around ViewData and exists only in ASP.NET MVC 3.
This being said, none of those two constructs should ever be used. You should use view models and strongly typed views. So the correct pattern is the following:
View model:
public class MyViewModel
{
public string Foo { get; set; }
}
Action:
public Action Foo()
{
var model = new MyViewModel { Foo = "bar" };
return View(model);
}
Strongly typed view:
#model MyViewModel
#Model.Foo
After this brief introduction let's answer your question:
My requirement is I want to set a value in a controller one, that
controller will redirect to ControllerTwo and Controller2 will render
the View.
public class OneController: Controller
{
public ActionResult Index()
{
TempData["foo"] = "bar";
return RedirectToAction("index", "two");
}
}
public class TwoController: Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Foo = TempData["foo"] as string
};
return View(model);
}
}
and the corresponding view (~/Views/Two/Index.cshtml):
#model MyViewModel
#Html.DisplayFor(x => x.Foo)
There are drawbacks of using TempData as well: if the user hits F5 on the target page the data will be lost.
Personally I don't use TempData neither. It's because internally it uses Session and I disable session in my applications. I prefer a more RESTful way to achieve this. Which is: in the first controller action that performs the redirect store the object in your data store and user the generated unique id when redirecting. Then on the target action use this id to fetch back the initially stored object:
public class OneController: Controller
{
public ActionResult Index()
{
var id = Repository.SaveData("foo");
return RedirectToAction("index", "two", new { id = id });
}
}
public class TwoController: Controller
{
public ActionResult Index(string id)
{
var model = new MyViewModel
{
Foo = Repository.GetData(id)
};
return View(model);
}
}
The view stays the same.
TempData
Basically it's like a DataReader, once read, data will be lost.
Check this Video
Example
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
TempData["T"] = "T";
return RedirectToAction("About");
}
public ActionResult About()
{
return RedirectToAction("Test1");
}
public ActionResult Test1()
{
String str = TempData["T"]; //Output - T
return View();
}
}
If you pay attention to the above code, RedirectToAction has no impact over the TempData until TempData is read. So, once TempData is read, values will be lost.
How can i keep the TempData after reading?
Check the output in Action Method Test 1 and Test 2
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
TempData["T"] = "T";
return RedirectToAction("About");
}
public ActionResult About()
{
return RedirectToAction("Test1");
}
public ActionResult Test1()
{
string Str = Convert.ToString(TempData["T"]);
TempData.Keep(); // Keep TempData
return RedirectToAction("Test2");
}
public ActionResult Test2()
{
string Str = Convert.ToString(TempData["T"]); //OutPut - T
return View();
}
}
If you pay attention to the above code, data is not lost after RedirectToAction as well as after Reading the Data and the reason is, We are using TempData.Keep(). is that
In this way you can make it persist as long as you wish in other controllers also.
ViewBag/ViewData
The Data will persist to the corresponding View
ViewBag, ViewData, TempData and View State in MVC
http://royalarun.blogspot.in/2013/08/viewbag-viewdata-tempdata-and-view.html
ASP.NET MVC offers us three options ViewData, VieBag and TempData for passing data from controller to view and in next request. ViewData and ViewBag are almost similar and TempData performs additional responsibility.
Similarities between ViewBag & ViewData :
Helps to maintain data when you move from controller to view. Used to
pass data from controller to corresponding view. Short life means
value becomes null when redirection occurs. This is because their goal
is to provide a way to communicate between controllers and views. It’s
a communication mechanism within the server call.
Difference between ViewBag & ViewData:
ViewData is a dictionary of objects that is derived from
ViewDataDictionary class and accessible using strings as keys. ViewBag
is a dynamic property that takes advantage of the new dynamic features
in C# 4.0. ViewData requires typecasting for complex data type and
check for null values to avoid error. ViewBag doesn’t require
typecasting for complex data type.
ViewBag & ViewData Example:
public ActionResult Index()
{
ViewBag.Name = "Arun Prakash";
return View();
}
public ActionResult Index()
{
ViewData["Name"] = "Arun Prakash";
return View();
}
In View, we call like below:
#ViewBag.Name
#ViewData["Name"]
TempData:
Helps to maintain data when you move from one controller to other
controller or from one action to other action. In other words when you
redirect, “Tempdata” helps to maintain data between those redirects.
It internally uses session variables. TempData is meant to be a very
short-lived instance, and you should only use it during the current
and the subsequent requests only
The only scenario where using TempData will reliably work is when you are redirecting. This is because a redirect kills the current request (and sends HTTP status code 302 Object Moved to the client), then creates a new request on the server to serve the redirected view.
It requires typecasting for complex data type and check for null values to avoid error.
public ActionResult Index()
{
var model = new Review()
{
Body = "Start",
Rating=5
};
TempData["ModelName"] = model;
return RedirectToAction("About");
}
public ActionResult About()
{
var model= TempData["ModelName"];
return View(model);
}
void Keep()
Calling this method with in the current action ensures that all the items in TempData are not removed at the end of the current request.
#model MyProject.Models.EmpModel;
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "About";
var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
TempData.Keep(); // retains all strings values
}
void Keep(string key)
Calling this method with in the current action ensures that specific item in TempData is not removed at the end of the current request.
#model MyProject.Models.EmpModel;
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "About";
var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
TempData.Keep("emp"); // retains only "emp" string values
}
TempData
will be always available until first read, once you read it its not available any more can be useful to pass quick message also to view that will be gone after first read.
ViewBag
Its more useful when passing quickly piece of data to the view, normally you should pass all data to the view through model , but there is cases when you model coming direct from class that is map into database like entity framework
in that case you don't what to change you model to pass a new piece of data, you can stick that into the viewbag
ViewData is just indexed version of ViewBag and was used before MVC3
Also the scope is different between viewbag and temptdata. viewbag is based on first view (not shared between action methods) but temptdata can be shared between an action method and just one another.
I have an app with many widgets and their content depends on the user requesting specific route. Simply put: if widget action is requested, its content must be rendered, otherwise it's empty. Consider routes/actions like this:
~/MyApp/Index -> without model; app HTML, without any widgets
~/MyApp/Foo/{id} -> uses FooModel; if ModelState is valid, returns
Index HTML with injected partial view of Foo's widget to div#foo;
otherwise redirects to Index.
~/MyApp/Bar/{id} -> same as Foo, but different model and widget
My foo action :
public ActionResult Foo(string id) {
if (ModelState.IsValid) {
var response = FooService.GetData(id);
// Inject Foo widget to Index
}
return RedirectToAction("Index");
}
I know that it is possible to use ViewBag or other means to send variables and using the condition to decide whether to render partial view or not. But... there should be a better way to do this, right?
I use MVC's Html.RenderActionResult when I want to build shared views with non-trivial binding logic (calling the database, composing complex objects, etc). The binding logic for each widget is contained in a PartialViewResult method, which is called from the *.cshtml file using Html.RenderAction().
ContentController:
public ActionResult Index(int id)
{
var indexViewModel = new IndexViewModel
{
Id = id,
Title = "My Title",
SubHeader = "Wow its 2016"
};
return View(indexViewModel);
}
public PartialViewResult PopularContent(int id)
{
var popularContentViewModel = new List<PopularContentViewModel>();
// query by id to get popular content items
return PartialView("_PopularContent", popularContentViewModel);
}
public PartialViewResult Widget2(int id)
{
return PartialView("_Widget2Partial");
}
Index.cshtml:
#model StackOverflow.RenderAction.ViewModels.IndexViewModel
<h1>#Model.Title</h1>
<h2>#Model.SubHeader</h2>
--RenderAction will call out to the specified route.
--Note the use of the Id parameter from the viewmodel.
#{Html.RenderAction("PopularContent", "Content", new {Model.Id});}
ASP.NET MVC Attribute Routing could a be a nice solution for this:
In your controller:
public class WidgetController : Controller
{
[Route("myapp/foowidget", Name = "FooWidget")]
public ActionResult FooWidget()
{
//create any model and return any view or partial or redirect
}
[Route("myapp/boowidget/{id:int}", Name = "BooWidget")]
public ActionResult BooWidget(int id)
{
//create any model and return any view or partial or redirect
}
}
And then in a View, you can call the Route by name:
#Url.RouteUrl("FooWidget")
or
#Url.RouteUrl("BooWidget")
or
#Html.RenderPartial("FooWidget")
#Url.RouteUrl("BooWidget") will render or concatenate the id that is in current url, if url is /myapp/something/id, because of your Route attribute definition: "myapp/boowidget/{id:int}". In fact #Url.RouteUrl("BooWidget") might extract the id from any current url of the format /controllerName/action/id, though you will have to test for sure.
And notice how you can have a separation of concerns with your WidgetController and your url Routes are not dependent on that controller's name in any way. That is a nice feature of Attribute Routing, you can declare custom routes as well as organize your controllers and break from nameing convention dependency of a controllerName being part of the url controllerName/action a user sees in their browser.
In regards to Html.RenderPartial, I am not sure if RenderPartial "connects" or will be able to route to your RouteName like "FooWidget". If it does great.
If not your solution is this:
public class WidgetController : Controller
{
public ActionResult FooWidget()
{
//model, you choose, return a partial
}
public ActionResult RedirectUser()
{
//do a redirect
}
public ActionResult BooWidget()
{
//any model, any partial
}
public ActionResult BooWidget(int id)
{
//any model, any partial
}
}
Each method in your controller is single purpose, has a distinct signature and does one thing, no conditions to pass in and no decisions required.
I'm faced with the following problem :
I have a controller with lets say the following actions:
[HttpGet]
public ActionResult Index()
{
var viewModel = new IndexViewModel();
return View("Index", viewModel);
}
[HttpPost]
public void ExportToExcell(LeadsViewModel model)
{
// Export to excell code goes here
}
The problem is the following:
The User enters on Index page with this URL : /Controller/Index
Then the user submits the form to Action ExportToExcel
Data is exported to Excel( file downloaded ) and it's okay.
The URL becomes /Controller/ExportToExcell
Then when I am clicking "Enter" I am going To /Controller/ExportToExcell but with GET
and of course falling with Page Not Found, the question is how properly to Deal with this in MVC
Don't use void as returned type of your post action, use an ActionResult
[HttpPost]
public ActionResult ExportToExcell(LeadsViewModel model)
{
// Export to excell code goes here
return RedirectToAction("Index");
}
I believe that your problem is that you aren't returning a FileResult, and the browser will redirect you to your post path. Can't test it right now, but I believe the following should work.
[HttpPost]
public ActionResult ExportToExcell(LeadsViewModel model)
{
// Generate the Excel file into a MemoryStream for example
// Return a FileResult with the Excel mime type
return File(fileStream, "application/vnd.ms-excel", "MyExcelFile.xls");
}
Check FileResult and Controller.File for more details.
As a note, I'm not completely sure if that's the mime type for an Excel file, but if you say you are already downloading the file, your probably already have it :)
You must return ActionResult instead of void.
public ActionResult ExportToExcel(PagingParams args)
{
var data = new StudentDataContext().Student.Take(200).ToList();
return data.GridExportToExcel<Student>("GridExcel.xlsx", ExcelVersion.Excel2007, args.ExportOption);
}
Please check the link: Export Action
Image the following controller method:
public ActionResult ShipmentDetails(Order order)
{
return View(new OrderViewModel { Order = order });
}
The incoming order parameter is filled from a custom model binder, that either creates a new order for this session and stores it in the session, or reuses an existing order from the current session. This order instace is now used to fill a shipment details form, where users can enter their address and so on.
When using #using(Html.BeginForm()) in the view. I cannot use the same signature for the post method again (because this would result in ambigious method names) and I found me adding a dummy parameter just to make this work.
[HttpPost]
public ActionResult ShipmentDetails(Order order, object dummy)
{
if (!ModelState.IsValid)
return RedirectToAction("ShipmentDetails");
return RedirectToAction("Initialize", order.PaymentProcessorTyped + "Checkout");
}
What are the best practices for this? Would you simply rename the method to something like PostShipmentDetails() and use one of the overloads of BeginForm? Or does the problem originate from the point, that the first method has the order parameter?
You could use the ActionName attribuite:
[HttpPost]
[ActionName("ShipmentDetails")]
public ActionResult UpdateShipmentDetails(Order order) { ... }
or a more classic pattern:
public ActionResult ShipmentDetails(int orderId)
{
var order = Repository.GetOrder(orderId);
return View(new OrderViewModel { Order = order });
}
[HttpPost]
public ActionResult ShipmentDetails(Order order)
{
if (!ModelState.IsValid)
return RedirectToAction("ShipmentDetails");
return RedirectToAction("Initialize", order.PaymentProcessorTyped + "Checkout");
}