MVC basecontroller doesn't return to controller after Initializing ASP.NET - asp.net

First off all i'm fairly new to C# and ASP.NET (mainly program java).
I've got a BaseController where i want to fill a viewbag for all my other controllers to use. I also set some session data and create some cookies. This is the basecontroller:
public abstract partial class BaseController : Controller
{
// GET: Base
protected override void Initialize(RequestContext requestContext)
{
try
{
//Checks if the user is logged in
if (requestContext.HttpContext.Session["customer"] != null)
{
ViewBag.Customer = requestContext.HttpContext.Session["customer"];
ViewBag.Points = requestContext.HttpContext.Session["points"];
ViewBag.CardNumber = requestContext.HttpContext.Session["cardNumber"];
}
//Gets the products to be displayed
var products = ProductList.Instance.AsQueryable();
ViewBag.Products = products;
//Checks to see if the user has a cart added to his requestContext.HttpContext.Session
if (requestContext.HttpContext.Session["cart"] == null)
{
requestContext.HttpContext.Session["cart"] = new Cart();
}
Cart cart = (Cart)requestContext.HttpContext.Session["cart"];
ViewBag.CartCount = cart.Count();
if (requestContext.HttpContext.Session["ticketID"] == null)
{
requestContext.HttpContext.Session["ticketID"] = Guid.NewGuid();
}
//Adds a cookie to the user with his selected theme
HttpCookie cookieUserTheme = requestContext.HttpContext.Request.Cookies["cookieUserTheme"];
if (cookieUserTheme != null)
{
requestContext.HttpContext.Session["UserPref"] = UserModel.GetThemeByName(cookieUserTheme.Value);
}
else
{
requestContext.HttpContext.Session["UserPref"] = UserModel.GetThemeByName("5");
var cookie = new HttpCookie("cookieUserTheme", ((UserPref)requestContext.HttpContext.Session["UserPref"]).ID);
cookie.Expires = DateTime.Now.AddDays(90);
requestContext.HttpContext.Response.Cookies.Add(cookie);
}
ViewBag.UserPref = requestContext.HttpContext.Session["UserPref"];
}
catch (Exception ex)
{
throw ex;
}
}
}
}
And this is the controller.
public class AdminController : BaseController
{
// GET: Admin
public ActionResult Index()
{
ViewBag.Themes = SiteMethods.GetAllThemes();
return View();
}
But when this is done running it just goes to the following ASP.NET page
What am i doing wrong? Do i need a redirect from my basecontroller?

If you're overriding Controller.Initialize() with your own initialization logic, you need to call base.Initialize(requestContext) to continue with the regular initialization process:
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
// rest of your code
// ...
}
Otherwise, this.ControllerContext (that is being used internally by several properties), would be null.
See Source

Related

Disable outputcache programmically before it happens that isn't an exception

Is there a way to disable the outputcache here programmatically if something happens that is not an exception?
[OutputCache(CacheProfile = "StatisticSheets")]
public virtual ActionResult GameStatistics(int? eventId, int? divisionId, string ids)
{
If(true) {
// Don't Cache This Page
}
return View();
}
This is how i have done:
create a derive class from outputcache:
public class MyOutputCache : OutputCacheAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
// checking route data for special condition
if(!filterContext.RouteData.Values.TryGetValue("abortcaching",out object _))
{
base.OnResultExecuting(filterContext);
}
}
}
then in controller:
[MyOutputCache(Duration = 60, VaryByParam = "none")]
public ActionResult Index()
{
ViewBag.Title = "Home Page " + DateTime.Now.Minute.ToString();
// it can be your condition
if (DateTime.Now.Minute %2 == 0)
{
RouteData.Values.Add("abortcaching", "true");
}
return View();
}
hope it helps.
If you need global to disable then read the below details, at the end also I shared with action method implementation code.
First, we want to capture whether or not the app is in debugging mode when it is launched. We'll store that in a global variable to keep things speedy.
public static class GlobalVariables
{
public static bool IsDebuggingEnabled = false;
}
Then in the Global.asax code's Application_Start method, write to the global property.
protected void Application_Start()
{
SetGlobalVariables();
}
private void SetGlobalVariables()
{
CompilationSection configSection = (CompilationSection)ConfigurationManager
.GetSection("system.web/compilation");
if (configSection?.Debug == true)
{
GlobalVariables.IsDebuggingEnabled = true;
}
}
Now we will create our own class to use for caching, which will inherit from OutputCacheAttribute.
public class DynamicOutputCacheAttribute : OutputCacheAttribute
{
public DynamicOutputCacheAttribute()
{
if (GlobalVariables.IsDebuggingEnabled)
{
this.VaryByParam = "*";
this.Duration = 0;
this.NoStore = true;
}
}
}
Now when you decorate your controller endpoints for caching, simply use your new attribute instead of [OutputCache].
// you can use CacheProfiles or manually pass in the arguments, it doesn't matter.
// either way, no caching will take place if the app was launched with debugging
[DynamicOutputCache(CacheProfile = "Month")]
public ViewResult contact()
{
return View();
}
With Action Method
-> For .net Framework: [OutputCache(NoStore = true, Duration = 0)]
-> For .net Core: [ResponseCache(NoStore = true, Duration = 0)]
You can use the particular action method above way

How to populate ViewDataDictionary inside an ExceptionFilter

How to retrieve the ViewModel from within an exception filter?
I have an ExceptionFilter, which I am using for a global error handler in an asp .net core 3.1 MVC application. I am trying to get the exception filter to redirect back to the View when there is an error and show validation errors, ie the equivalent of saying:
return View(viewModel)
in the controller
I can redirect to the View, but am a little stuck on how to populate the Model in the ViewResult
ExceptionFilter code
public void OnException(ExceptionContext context)
{
string controller = context.RouteData.Values["controller"].ToString();
string action = context.RouteData.Values["action"].ToString();
if (context.Exception is WebServiceException && context.Exception.IsUnauthorized())
{
context.Result = new RedirectToActionResult("fetchtoken", "Home", new { path = $"/{controller}/{action}" });
}
//other type of exception, return the view displaying errors
else
{
context.ModelState.Clear();
context.ModelState.AddModelError(action, $"error in {action}");
m_Logger.LogError(context.Exception, $"error in {action}");
context.ExceptionHandled = true;
context.ModelState
context.Result = new ViewResult{
ViewName = action,
ViewData = // ??????????????
};
}
}
In the controller:
[Authorize]
[HttpPost]
public async Task<IActionResult> AuthoriseApiUser(AuthoriseApiViewModel viewModel)
{
await m_ApiUserService.AuthoriseUser(viewModel.TenantId, viewModel.UserId); //error thrown here
return View(viewModel);
}
Through obtaining the value of each key in the form data, the value is compared with the property of the model. Then, assign value to model. For example.
public void OnException(ExceptionContext context)
{
string controller = context.RouteData.Values["controller"].ToString();
string action = context.RouteData.Values["action"].ToString();
//start
var viewModel = new ViewModel();
var list = context.HttpContext.Request.Form.AsEnumerable();
foreach (var meta in list)
{
if (meta.Key == "addr")
{
viewModel.addr = meta.Value;
}
}
//end
if (context.Exception is WebServiceException && context.Exception.IsUnauthorized())
{
context.Result = new RedirectToActionResult("fetchtoken", "Home", new { path = $"/{controller}/{action}" });
}
//other type of exception, return the view displaying errors
else
{
//...
var modelMetadata = new EmptyModelMetadataProvider();
context.Result = new ViewResult
{
ViewName = action,
ViewData = ViewData = new ViewDataDictionary(modelMetadata, context.ModelState)
{
Model = viewModel
}
};
}
}
Model
public class ViewModel
{
public int id { get; set; }
[MinLength(2)]
public string addr { get; set; }
}

How to create dynamic role in asp.net mvc5

I want to create a dynamic role in ASP.NET MVC 5. I do not want to create hardcode roles in the authorization attribute .I want to create roles later.it's a test for my recruitment.Do you have sample code or video In this case?
Just in ASP.NET MVC 5.
Thanks in advance for your help
You mean you need dynamic authorization.
In order to do this.
1.You need to add two more tables(Except identity tables).
AppContent (Columns:{Id, Resource, Function,Description})
RoleRights (Columns:{Id, RoleName,AppContentId).
2.Create CustomAuthorizeAttribute
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class CustomAuthorize : AuthorizeAttribute
{
//Custom named parameters for annotation
public string Source { get; set; }//Controller Name
public string Function { get; set; }//Action Name
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Is user logged in?
if (httpContext.User.Identity.IsAuthenticated)
{
if ((!string.IsNullOrEmpty(ResourceKey)) && (!string.IsNullOrEmpty(OperationKey)))
{
//There are many ways to store and validate RoleRights
//1.You can store in Database and validate from Database.
//2.You can store in user claim at the time of login and validate from UserClaims.
//3.You can store in session validate from session
//Below I am using database approach.
var loggedInUserRoles = ((ClaimsIdentity) httpContext.User.Identity).Claims
.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value);
//logic to check loggedInUserRoles has rights or not from RoleRights table
return db.RoleRights.Any( x=> x.AppContent.Source == Source && x.AppContent.Function == Function && loggedInUserRoles.Contains( x.AppContent.RoleName));
}
}
//Returns true or false, meaning allow or deny. False will call HandleUnauthorizedRequest above
return base.AuthorizeCore(httpContext);
}
//Called when access is denied
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//User isn't logged in
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
return;
}
//User is logged in but has no access
else
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(new { controller = "Account", action = "NotAuthorized" })
);
}
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
// Check for authorization
if (string.IsNullOrEmpty(this.Source) && string.IsNullOrEmpty(this.Function))
{
this.Source = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
this.Function = filterContext.ActionDescriptor.ActionName;
}
base.OnAuthorization(filterContext);
}
}
3. Assign CustomAuthorizeAttribute to the Controller Action
[CustomAuthorize(Source= "Branch", Function = "Index")]
public ActionResult Index()
{
return View(model);
}
[CustomAuthorize(Source = "Branch", Function = "Details")]
public ActionResult Details(long? id)
{
return View(branch);
}
[CustomAuthorize(Source = "Branch", Function = "Create")]
public ActionResult Create()
{
return View();
}
4.Setup all of your application content like Source(Controller) and Function(Action) in AppContent table.
5.Assign AppContents to a role for allowing to role to access this content.
6.Assign User to Role.
7.Run the application and test.

How to return a view for HttpNotFound() in ASP.Net MVC 3?

Is there a way to return the same view every time a HttpNotFoundResult is returned from a controller? How do you specify this view? I'm guessing configuring a 404 page in the web.config might work, but I wanted to know if there was a better way to handle this result.
Edit / Follow up:
I ended up using the solution found in the second answer to this question with some slight tweaks for ASP.Net MVC 3 to handle my 404s: How can I properly handle 404s in ASP.Net MVC?
HttpNotFoundResult doesn't render a view. It simply sets the status code to 404 and returns an empty result which is useful for things like AJAX but if you want a custom 404 error page you could throw new HttpException(404, "Not found") which will automatically render the configured view in web.config:
<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="/Http404.html" />
</customErrors>
This solution combines IResultFilter and IExceptionFilter to catch either thrown HttpException or returned HttpStatusCodeResult from within an action.
public class CustomViewForHttpStatusResultFilter: IResultFilter, IExceptionFilter
{
string viewName;
int statusCode;
public CustomViewForHttpStatusResultFilter(HttpStatusCodeResult prototype, string viewName)
: this(prototype.StatusCode, viewName) {
}
public CustomViewForHttpStatusResultFilter(int statusCode, string viewName) {
this.viewName = viewName;
this.statusCode = statusCode;
}
public void OnResultExecuted(ResultExecutedContext filterContext) {
HttpStatusCodeResult httpStatusCodeResult = filterContext.Result as HttpStatusCodeResult;
if (httpStatusCodeResult != null && httpStatusCodeResult.StatusCode == statusCode) {
ExecuteCustomViewResult(filterContext.Controller.ControllerContext);
}
}
public void OnResultExecuting(ResultExecutingContext filterContext) {
}
public void OnException(ExceptionContext filterContext) {
HttpException httpException = filterContext.Exception as HttpException;
if (httpException != null && httpException.GetHttpCode() == statusCode) {
ExecuteCustomViewResult(filterContext.Controller.ControllerContext);
// This causes ELMAH not to log exceptions, so commented out
//filterContext.ExceptionHandled = true;
}
}
void ExecuteCustomViewResult(ControllerContext controllerContext) {
ViewResult viewResult = new ViewResult();
viewResult.ViewName = viewName;
viewResult.ViewData = controllerContext.Controller.ViewData;
viewResult.TempData = controllerContext.Controller.TempData;
viewResult.ExecuteResult(controllerContext);
controllerContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
You can register this filter so, specifying either the http status code of the HttpException or the concrete HttpStatusCodeResult for which you want to display the custom view.
GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(new HttpNotFoundResult(), "Error404"));
// alternate syntax
GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(404, "Error404"));
It handles exceptions and HttpStatusCodeResult thrown or returned within an action. It won't handle errors that occur before MVC selects a suitable action and controller like this common problems:
Unknown routes
Unknown controllers
Unknown actions
For handling these types of NotFound errors, combine this solution with other solutions to be found in stackoverflow.
Useful info from #Darin Dimitrov that HttpNotFoundResult is actually returning empty result.
After some study. The workaround for MVC 3 here is to derive all HttpNotFoundResult, HttpUnauthorizedResult, HttpStatusCodeResult classes and implement new (overriding it) HttpNotFound() method in BaseController.
It is best practise to use base Controller so you have 'control' over all derived Controllers.
I create new HttpStatusCodeResult class, not to derive from ActionResult but from ViewResult to render the view or any View you want by specifying the ViewName property. I follow the original HttpStatusCodeResult to set the HttpContext.Response.StatusCode and HttpContext.Response.StatusDescription but then base.ExecuteResult(context) will render the suitable view because again I derive from ViewResult. Simple enough is it? Hope this will be implemented in the MVC core.
See my BaseController bellow:
using System.Web;
using System.Web.Mvc;
namespace YourNamespace.Controllers
{
public class BaseController : Controller
{
public BaseController()
{
ViewBag.MetaDescription = Settings.metaDescription;
ViewBag.MetaKeywords = Settings.metaKeywords;
}
protected new HttpNotFoundResult HttpNotFound(string statusDescription = null)
{
return new HttpNotFoundResult(statusDescription);
}
protected HttpUnauthorizedResult HttpUnauthorized(string statusDescription = null)
{
return new HttpUnauthorizedResult(statusDescription);
}
protected class HttpNotFoundResult : HttpStatusCodeResult
{
public HttpNotFoundResult() : this(null) { }
public HttpNotFoundResult(string statusDescription) : base(404, statusDescription) { }
}
protected class HttpUnauthorizedResult : HttpStatusCodeResult
{
public HttpUnauthorizedResult(string statusDescription) : base(401, statusDescription) { }
}
protected class HttpStatusCodeResult : ViewResult
{
public int StatusCode { get; private set; }
public string StatusDescription { get; private set; }
public HttpStatusCodeResult(int statusCode) : this(statusCode, null) { }
public HttpStatusCodeResult(int statusCode, string statusDescription)
{
this.StatusCode = statusCode;
this.StatusDescription = statusDescription;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
context.HttpContext.Response.StatusCode = this.StatusCode;
if (this.StatusDescription != null)
{
context.HttpContext.Response.StatusDescription = this.StatusDescription;
}
// 1. Uncomment this to use the existing Error.ascx / Error.cshtml to view as an error or
// 2. Uncomment this and change to any custom view and set the name here or simply
// 3. (Recommended) Let it commented and the ViewName will be the current controller view action and on your view (or layout view even better) show the #ViewBag.Message to produce an inline message that tell the Not Found or Unauthorized
//this.ViewName = "Error";
this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
base.ExecuteResult(context);
}
}
}
}
To use in your action like this:
public ActionResult Index()
{
// Some processing
if (...)
return HttpNotFound();
// Other processing
}
And in _Layout.cshtml (like master page)
<div class="content">
#if (ViewBag.Message != null)
{
<div class="inlineMsg"><p>#ViewBag.Message</p></div>
}
#RenderBody()
</div>
Additionally you can use a custom view like Error.shtml or create new NotFound.cshtml like I commented in the code and you may define a view model for the status description and other explanations.
protected override void HandleUnknownAction(string actionName)
{
ViewBag.actionName = actionName;
View("Unknown").ExecuteResult(this.ControllerContext);
}
Here is true answer which allows fully customize of error page in single place.
No need to modify web.confiog or create sophisticated classes and code.
Works also in MVC 5.
Add this code to controller:
if (bad) {
Response.Clear();
Response.TrySkipIisCustomErrors = true;
Response.Write(product + I(" Toodet pole"));
Response.StatusCode = (int)HttpStatusCode.NotFound;
//Response.ContentType = "text/html; charset=utf-8";
Response.End();
return null;
}
Based on http://www.eidias.com/blog/2014/7/2/mvc-custom-error-pages
Please follow this if you want httpnotfound Error in your controller
public ActionResult Contact()
{
return HttpNotFound();
}

The IControllerFactory 'MyWebSite.WebUI.Infrastructure.NinjectControllerFactory' did not return a controller for the name 'Admin'

I am getting the above when I try and open a view in a controller in an Area. Ninject is set up as follows:
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel kernel = new StandardKernel(new RLSBCWebSiteServices());
protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
if (controllerType == null)
return null;
return (IController)kernel.Get(controllerType);
}
private class MyWebSiteServices : NinjectModule
{
public override void Load()
{
Bind<IMatchesRepository>().To<SqlMatchesRepository>().WithConstructorArgument("connectionString",
ConfigurationManager.ConnectionStrings["MyWebSiteDb"].ConnectionString);
}
}
}
If I place a breakpoint in the code, I see the RequestContext context contains the following values:
context.RouteData.DataTokens.Values[0] = “MyWebSite.WebUI.Areas.Visitor” context.RouteData.DataTokens.Values[1] = “Visitor” which is the Area
context.RouteData.Values.Values[0] = “admin” which is the Controller
context.RouteData.Values.Values[1] = “register” which is the View
However controllerType == null, instead on the controller name.
This transfer to the new page is being triggered by
Html.ActionLink("here", "Register", "Admin", new { area = "Visitor" }, null)
which is on the Login page. However the same thing happens if I enter
http://example.com/Visitor/admin/register
into IE8
The area registration is as follows:
public class VisitorAreaRegistration : AreaRegistration
{
public override string AreaName { get { return "Visitor"; } }
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Visitor_default",
"Visitor/{controller}/{action}/{id}",
new { controller = "Admin", action = "Register", id = UrlParameter.Optional }
);
}
}
Has anyone managed to get Areas working with NinjectControllerFactory, or is there something wrong with my set-up?
Instead of creating your own NinjectControllerFactory use the latest version of Ninject.Web.Mvc. It supports Areas. See: https://github.com/ninject/ninject.web.mvc
Check your controller and action name in view. I also got same error as an action name was wrong.

Resources