Base controller class - asp.net-mvc

I have a base controller class and I would like to pass a Message from the Base class to all controllers and for that message to be available to all views.
I've created a basic version below...
Section Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Website.Controllers
{
public class SectionController : Controller
{
//
// GET: /Section/
public ActionResult Section()
{
ViewData["Message"] = "THIS IS A TEST";
return View();
}
}
}
Home Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Website.Controllers
{
public class HomeController : SectionController
{
public ActionResult Index()
{
return View();
}
}
}
View
<%= Html.Encode(ViewData["Message"]) %>
I know I can do this in the home controller but I'm just testing at the mo.
I'm not getting any errors with the above but I'm also not displaying the message on my view?
I'm using this tutorial http://www.asp.net/LEARN/mvc/tutorial-13-cs.aspx The Good Solution part, if that helps.
Think I've got it working now used the code below on my sectionController...
namespace Website.Controllers
{
public class SectionController : Controller
{
//
// GET: /Section/
public SectionController()
{
ViewData["Message"] = "THIS IS A TEST";
//return View();
}
}
}
Is this an ok solution?

You're setting your ViewData in the Section action method of your base controller, do you actually want to be setting it in the constructor of your base controller?
public SectionController()
{
ViewData["Message"] = "THIS IS A TEST";
}

HomeController.Index isn't calling SectionController.Section.

Because none of the requests are mapped to action "Section" in SectionController. If you mapped a request like domain/Section/Section, you would see your message in your view (Assuming that you are using default routing and have a view named "Section").
What you need to do is, placing your message into the viewdata on a method that runs every time an action is run. You can do it in OnActionExecuting like:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
ViewData["Message"] = "THIS IS A TEST";
base.OnActionExecuting(filterContext);
}
in the SectionController.

Related

Why does RedirectToActionResult ControllerName return NULL?

I have a Xunit test class for a MVC:
using System;
using Xunit;
using Home.Controllers;
using Microsoft.AspNetCore.Mvc;
using Tester.Models;
using Xunit.Abstractions;
namespace ClassLibrary1
{
public class Class1
{
[Fact]
public void CreateinController()
{
HomeController c = new HomeController();
var result = c.Create();
//result moet een view zijn
var viewResult = Assert.IsType<ViewResult>(result);
//Juiste redirection
var result2 = c.Create(null);
var redirectToActionResult =
Assert.IsType<RedirectToActionResult>(result2);
Assert.Equal("Home", redirectToActionResult.ControllerName);
Assert.Equal("BoekingVerwerken", redirectToActionResult.ActionName);
}
}
}
Whenever I run the test the ActionName passes the test because it actually contains the value. But the ControllerName seems to be NULL valued, why is this?
When the Action has a value why wouldn't the Controller have one? Should I put the controller in the RTA?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Tester.Models;
namespace Home.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(HotelBoeking boeking)
{
return RedirectToAction("BoekingVerwerken", boeking);
}
[HttpGet]
public IActionResult BoekingVerwerken(HotelBoeking boeking)
{
return View(boeking);
}
}
}
If you take a look at the Controller.cs source code, you will see that RedirectToAction returns a RedirectToRouteResult type, not a RedirectToActionResult type.
As you can see from the source code of RedirectToRouteResult.cs, you can get the controller name from the RouteValues property. There is no ControllerName property on this type.
var redirectToRouteResult =
Assert.IsType<RedirectToRouteResult>(result2);
Assert.Equal("Home", redirectToRouteResult.RouteValues["controller"]);
I am not entirely sure the syntax for XUnit is correct, as I don't work with it much, but you should take away from this that because MVC is open source that questions like this one can be easily answered by inspecting the source code.

Database first MVC, object not populating

I have to use the Database first approach, the solution has 3 projects. 2 are class libraries the third is the actual MVC project. One class library has the edmx designer. The other has Has different model classes. These 2 libraries are referenced by the MVC project. My problem is that the object is not populating values. I guess it is a naming conflict somewhere. The object tblRDBUser is not getting populated. There is a table in the database with the same object name too. The object attributes match the column names too. I am not getting the values from the view into the controller.
Project one:
RDB.DataModel, it contains the EDMX designer.
Project two: RDB.Models
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RDB.DataModel;
public class Authenticate
{
public string AuthenticateUser(tblRDBUser User)
{
Book_TradEntities users = new Book_TradEntities();
return (from p in users.tblRDBUsers where p.UserName == User.UserName && p.Password == User.Password select p.UserName).FirstOrDefault();
}
}
Project 3:
View:
#model RDB.DataModel.tblRDBUser
#{
ViewBag.Title = "Index";
}
#Html.TextBoxFor(a => a.UserName)
#Html.TextBoxFor(a => a.Password)
#Html.ActionLink("Login", "Authenticate");
Controller:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using RDB.DataModel;
using RDB.Models;
namespace CignaRDB.Controllers
{
public class RDBWebController : Controller
{
//
// GET: /RDBWeb/
public ActionResult Index()
{
return View();
}
public ActionResult Authenticate(tblRDBUser User)
{
if (ModelState.IsValid)
{
Authenticate user = new Authenticate();
user.AuthenticateUser(User);
return View();
}
return View();
}
}
}
The main problem. that you create link #Html.ActionLink("Login", "Authenticate") which after click on him just redirect user to Authenticate method. Insted of a link you should generate button which make submit of you form. So your view code should be something like this
#model RDB.DataModel.tblRDBUser
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("Authenticate"))
{
#Html.TextBoxFor(a => a.UserName)
#Html.TextBoxFor(a => a.Password)
<input type="submit" value="Login" />
}
Also you controller looks strange, maybe because this is result of code simplification, but at least should be next change when your model is invalid (return View("Index");):
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using RDB.DataModel;
using RDB.Models;
namespace CignaRDB.Controllers
{
public class RDBWebController : Controller
{
//
// GET: /RDBWeb/
public ActionResult Index()
{
return View();
}
public ActionResult Authenticate(tblRDBUser User)
{
if (ModelState.IsValid)
{
Authenticate user = new Authenticate();
user.AuthenticateUser(User);
return View();
}
return View("Index");//because view name and Action method not match
}
}
}

Session across controller becomes null in MVC

My session becomes null when I redirect to another controller's Action, what should I do?
With regards to the comment you posted me, here is what I was thinking. In the Controller where you need the session use something similar to this:
//Controller A
public class TheController : Controller
{
public ActionResult Index(){
Session["yourSession"] = "Hello World";
return View();
}
}
//Controller B
public class MyController : Controller
{
string textString;
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
base.OnActionExecuting(ctx);
textString = ctx.HttpContext.Session["yourSession"].ToString();
}
public ActionResult Index(){
string currentText = textString;
return View();
}
}
I tested the suggestion from (http://stackoverflow.com/questions/889516/session-null-in-asp-net-mvc-controller-constructors), and the contents of the session were available.
You have to create a unique base controller with a session property, then all controllers within your project will inherit from that BaseController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MonitoringCSP.Controllers
{
//[AllowAnonymous]
//[Authorize(Roles = "admin")]
public class BaseController : Controller
{
private HttpSessionStateBase _session;
protected HttpSessionStateBase CrossControllerSession
{
get
{
if (_session == null) _session = Session;
return _session;
}
set {
_session = Session;
}
}
}
}
Usage sample
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using MonitoringCSP.Models;
namespace MonitoringCSP.Controllers
{
[AllowAnonymous]
public class AccountController : BaseController
{
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
/*session*/
CrossControllerSession["UserName"] = User.Identity.Name;
/*end session*/
return RedirectToAction("someAction");
}
}
}
I realized that I was clearing and destroying all sessions prior to setting the new session on login like this
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
Session.Abandon();
Session.Clear();
When I removed these lines, everything started working like #Clayton said, so I removed these lines and replaced it with Session.Remove('sessionvariablename'), I am still not sure what issue were the above lines causing, but my code started working.
Make sure your controller does not have this attribute set on it:
[SessionState(SessionStateBehavior.Disabled)]

asp.net MVC confused by custom attributes creation

hi i am trying to create a custom attribute for my MVC application so that i can call [CheckLogin] this is to check my cookie as i am not using forms authentification.
i have created a class CheckLogin and this is in my App_Code folder and the code is as follows:
using System.Web.Mvc;
using System.Attributes;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Web;
using System;
namespace corian_MVC.Controllers
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CheckLoginAttribute : FilterAttribute, IAuthorizationFilter
{
public CheckLoginAttribute() {}
public void OnAuthorization(AuthorizationContext filterContext)
{
// TODO: perform your cookie checks
if (!userIsAuthenticated)
{
filterContext.Result = new RedirectResult(string.Format(
"/Admin/Login",
filterContext.HttpContext.Request.Url.AbsoluteUri));
}
}
}
}
what it does is not important here, the problem is i cant get my code to recognise this attribute if it is one in the first place, also how do i redirect to action if the login is failed ????
many thanks
my admin class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
namespace corian_MVC.Controllers
{
[HandleError]
public class AdminController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
//check login is not banned
if ((int)Session["LoginCount"] >= 3) RedirectToAction("TooMany");
return View();
}
public ActionResult Fraud()
{
Session["LoginCount"] = 3;
return View();
}
public ActionResult TooMany()
{
return View();
}
[CheckLogin]
public ActionResult Welcome()
{
return View();
}
private void Createcookie()
{
}
}
}
This scenario is best handled by implementing an IAuthorizationFilter.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
public class CheckLoginAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
// TODO: perform your cookie checks
if (!userIsAuthenticated)
{
filterContext.Result = new RedirectResult(string.Format(
"/loginUrl?ReturnUrl={0}",
filterContext.HttpContext.Request.Url.AbsoluteUri));
}
}
}
Then you can apply this attribute either at the controller level or at some particular actions.
By the way do you have any particular reason for not using the built-in FormsAuthentication?
Include .cs file with your attribute to the solution. Just placing it "near default.aspx" is not enough.

How to create a strongly typed master page using a base controller in ASP.NET MVC

Following the NerdDinners example, I am interested in creating a strongly typed Master Page. In order to achieve this, I use a base controller which retrieves the data for the master page. All other controllers inherit this class. Similarly, I have ViewModels for the master page and any other views. The view ViewModel classes inherit from the master page's ViewModel.
Question
How should a child controller ensure that the master page's data is passed to the View without setting the properties of its ViewModel that pertain to the master page itself?
My the master page will display a number of buttons, which are determined in an XML file, hence the Buttons class that I am populating.
MasterPage ViewModel Code Snippet
using System.Collections.Generic;
namespace Site1.Models
{
public class MasterViewModel
{
public List<Button> Buttons{set; get;}
}
}
View ViewModel
namespace Site1.Models
{
public class View1ViewModel : MasterViewModel
{
public SomeDataClass SomeData { get; set; }
}
}
Base Controller
using System.Collections.Generic;
using System.Web.Mvc;
using Site1.Models;
namespace Site1.Controllers
{
public abstract class BaseController : Controller
{
protected MasterViewModel model = new MasterViewModel();
public BaseController()
{
model.Buttons = new List<Button>();
//populate the button classes (doesn't matter how)
PopulateButtons(model.Buttons);
}
}
}
View's controller:
using System.Web.Mvc;
namespace Site1.Controllers
{
public class View1Controller : BaseController
{
public ActionResult Index()
{
Models.View1ViewModel viewModel = new Models.View1ViewModel();
SomeDataClass viewData = new SomeDataClass()
//populate data class (doesn't matter how)
PopulateDataClass(viewData);
viewModel.SomeData = viewData;
//I WANT TO ELIMINATE THE FOLLOWING LINE!
viewModel.Buttons = model.Buttons;
return View("Index", viewModel);
}
}
}
The master page inherits System.Web.Mvc.ViewMasterPage<Site1.Models.MasterViewModel>.
The view inherits System.Web.Mvc.ViewMasterPage<Site1.Models.View1ViewModel>.
You could create an after action executed filter which looks for a model of that type and sets the properties accordingly, perhaps by calling a base controller function. You would then put the filter on the base class, and all actions would see it automatically.
The action filter attribute gets the controller's ViewModel, and passes it to the controller's SetModel function:
using System.Web.Mvc;
using Site1.Controllers;
namespace Site1.Models
{
public class MasterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
MasterViewModel viewModel = (MasterViewModel)((ViewResultBase)filterContext.Result).ViewData.Model;
BaseController controller = (BaseController)filterContext.Controller;
controller.SetModel(viewModel);
}
}
}
This function is added to the BaseController:
public void SetModel(MasterViewModel childViewModel)
{
childViewModel.Buttons = model.Buttons;
}
Rather than creating an attribute, why not just override Controller.OnActionExecuted and put the initialization code there? Seems a bit simpler.

Resources