public class BaseController : Controller
{
// GET: Base
public static UserManager.User _CurrentUser;
}
}
This code is part of my BaseController and I want to use _CurrrentUser.Id as key for outputcache.
[OutputCache(Duration = 1200, VaryByCustom = _CurrentUser.Id)]
When I tried to do this, it say "Argument in attribute must be constant exprssion" and it's also need to set to static.
I can make this property static but how I can make it constant expression so I can use it for outputcache.
I recommend you should get CurrentUserId from Auth. Cookie. I use like that.
[Authorize]
public class BaseController : Controller
{
private UserModel _currentUser;
public UserModel CurrentUser => _currentUser ?? (_currentUser = GetCurrentUser());
private UserModel GetCurrentUser()
{
UserModel currentUser;
if (!User.Identity.IsAuthenticated) return null;
try
{
var userDataFromCookie = CookieHelper.GetCookie(FormsAuthentication.FormsCookieName);
if (string.IsNullOrWhiteSpace(userDataFromCookie))
throw new ArgumentException("Authentication cookie is null");
currentUser = JsonHelper.Deserialize<UserModel>(FormsAuthentication.Decrypt(userDataFromCookie)?.UserData);
}
catch (Exception)
{
throw;
}
return currentUser;
}
}
Cookie Helper Method like that
public static string GetCookie(string key)
{
return HttpContext.Current.Request.Cookies[key] != null ? HttpContext.Current.Request.Cookies[key].Value : null;
}
Related
I have a BaseApiController which my controller inherits from. It overrides the Initialize method. The method will retrieve a JWT token from the HttpControllerContext and use it to retrieve the user making the request.
public class BaseApiController : ApiController
{
public static tUser CurrentUser;
public BaseApiController()
{
}
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
var request = controllerContext.Request;
if (request.Headers.Authorization != null && request.Headers.Authorization.Scheme.Equals("bearer", StringComparison.OrdinalIgnoreCase))
{
CurrentUser = Helpers.JwtAuthentication.UserToken(request.Headers.Authorization.Parameter);
}
}
}
The error happens, intermittently, when calling the UserToken method. Here is the method.
public static tUser UserToken(string token)
{
string username = ExtractUserName(token);
if (string.IsNullOrEmpty(username))
return null;
try
{
tUser user = Repository.DB.tUsers.Where(u => u.UserName == username && u.IsDeleted == false).FirstOrDefault();
return user;
}
catch (Exception ex)
{
return null;
}
}
The exception is thrown on the line tUser user = Repository.DB.tUsers.Where(u => u.UserName == username && u.IsDeleted == false).FirstOrDefault(); and I can't tell why. If I examine the various objects in the line of code they aren't null. If I execute the debugger over the line of code again it runs with no problems.
Why does this line of code intermittently throw error 'Object reference is not sent to an instance of an object'?
public class Repository
{
public static Entities DB = new Entities(ConfigurationManager.AppSettings["ConnectionString"].ToString());
}
public partial class Entities : DbContext
{
public Entities(string secret) : base(Helpers.KeyVault.GetSecret(secret))
{
this.Configuration.LazyLoadingEnabled = false;
}
}
As stated in comments, to ease search for others with similar issues:
"New up" dbContext directly in BaseApiController in using clock or with using statement (using var ctx = new Entities(string secret)) - this will do the trick.
Alternative would be to force Repository to always return new instance of dbContext by replaceing
public static Entities DB = new Entities(ConfigurationManager.AppSettings["ConnectionString"].ToString());
with a property
public static Entities DB => new Entities(ConfigurationManager.AppSettings["ConnectionString"].ToString());
It should resolve possible scope issue and do the trick if there is no problem with actually resolving custom dbContext class.
I have the following base controller with a string variable
public abstract class BaseController:Controller
{
string encryptedSessionGuid;
}
All other controller derives from base controller and ActionMethod has a custom ActionFilterAttribute CheckQueryString-
public class SampleController : BaseController
{
[CheckQueryString(new string[] {"sid"})]
public ActionResult SampleMethod()
{
return View();
}
}
Here is my custom attribute. It sends query string value to view. But I would like to send it base controller variable encryptedSessionGuid also.
public class CheckQueryString : ActionFilterAttribute
{
string[] keys;
public CheckQueryString(string[] Keys) { keys = Keys; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
foreach (var key in keys)
{
if (ctx.Request.QueryString[key] == null)
{
filterContext.Result = new RedirectResult(BulkSmsApplication.GlobalConfig.BaseUrl);
return;
}
else
{
string value = ctx.Request.QueryString[key];
if (string.IsNullOrEmpty(value))
{
filterContext.Result = new RedirectResult(BulkSmsApplication.GlobalConfig.BaseUrl);
return;
}
else
{
var viewBag = filterContext.Controller.ViewData;
viewBag[key] = value;
}
}
}
base.OnActionExecuting(filterContext);
}
}
How can it be done?
I have following implementation in my base controller from which I am deriving most of my controllers. It accounts for setting the page title, meta description and keywords for each page if the values are not set via decorator on the controller actions.
BaseController
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Page title
var title = filterContext.ActionDescriptor.GetCustomAttributes(typeof(PageTitleAttribute), false);
if (title.Length == 1)
ViewBag.Title = ((PageTitleAttribute)(title[0])).Parameter;
else
ViewBag.Title = "My website title";
//Page keywords
var keywords = filterContext.ActionDescriptor.GetCustomAttributes(typeof(MetaKeywordsAttribute), false);
if (keywords.Length == 1)
ViewBag.MetaKeywords = ((MetaKeywordsAttribute)(keywords[0])).Parameter;
else
ViewBag.MetaKeywords = "targeted SEO keywords";
//Page description
var description = filterContext.ActionDescriptor.GetCustomAttributes(typeof(MetaDescriptionAttribute), false);
if (description.Length == 1)
ViewBag.MetaDescription = ((MetaDescriptionAttribute)(description[0])).Parameter;
else
ViewBag.MetaDescription = "My custom description";
base.OnActionExecuting(filterContext);
}
}
The custom attributes are fairly simple:
public class PageTitleAttribute : Attribute
{
private readonly string _parameter;
public PageTitleAttribute(string parameter)
{
_parameter = parameter;
}
public string Parameter { get { return _parameter; } }
}
public class MetaDescriptionAttribute : Attribute
{
private readonly string _parameter;
public MetaDescriptionAttribute(string parameter)
{
_parameter = parameter;
}
public string Parameter { get { return _parameter; } }
}
public class MetaKeywordsAttribute : Attribute
{
private readonly string _parameter;
public MetaKeywordsAttribute(string parameter)
{
_parameter = parameter;
}
public string Parameter { get { return _parameter; } }
}
And this is how make use of the attributes on appropirate action methods in controller:
public class HomeController : BaseController
{
[PageTitle("My new website")]
[MetaKeywords("Explicitly set keywords")]
[MetaDescription("description goes here")]
public ActionResult Index()
{
return View();
}
public ActionResult Error()
{
return View();
}
}
This all seems to work just fine. Now I would like to create unit test to validate that if the values are not set via attribute on action methods, the default values will be rendered as set from base controller. How can I do that? I have some tests written but I don't think they are targeting the filterContext on the basecontroller. Specifically I am looking for test for Error action method which does not have anything attribute value set. Just for reference this is what I have setup now:
[TestMethod]
public void Attribute_when_set_should_return_attribute_values()
{
var method = typeof(HomeController).GetMethod("Index");
var pageTitle = method.GetCustomAttributes(typeof(PageTitleAttribute), false)
.Cast<PageTitleAttribute>()
.SingleOrDefault();
var keywords = method.GetCustomAttributes(typeof(MetaKeywordsAttribute), false)
.Cast<MetaKeywordsAttribute>()
.SingleOrDefault();
var description = method.GetCustomAttributes(typeof(MetaDescriptionAttribute), false)
.Cast<MetaDescriptionAttribute>()
.SingleOrDefault();
Assert.IsNotNull(pageTitle);
Assert.IsNotNull(keywords);
Assert.IsNotNull(description);
Assert.AreEqual("My new website", pageTitle.Parameter);
Assert.AreEqual("Explicitly set keywords", keywords.Parameter);
Assert.AreEqual("description goes here", description.Parameter);
}
I'm trying to return a action "PageNotFound" that resides in my "Error"-controller.
public class BaseController : Controller
{
public BaseController()
{
}
public BaseController(IContentRepository contentRep, ILocalizedRepository localRep)
{
this._localRep = localRep;
this._contentRep = contentRep;
}
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;
}
this.ViewName = "PageNotFound"; // CONTROLLER MISSING
this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
base.ExecuteResult(context);
}
}
How can I modify it so it returns the "PageNotFound" action in the "Error"- controller?
A ViewResult is supposed to directly render a view (optionally passing a model and a layout). There's no controller involved in this process.
If you want to go through a controller you need to perform redirect, i.e. use RedirectToRouteResult instead of ViewResult.
In your example you are using this custom ViewResult directly inside some other controller. So that will be the controller that will render the error view.
I dont understand why you want to make a redirect. I would return 404
return HttpStatusCode(404);
And then use the approach described here: ASP.NET MVC 404 Error Handling to render the correct view. Benefit: your url is still the same, much easier for error handling and for the browser history.
Have you tried
return RedirectToAction("PageNotFound", "ControllerName");
I have the following method in five controllers:
public ActionResult Index(string page, string title) {
var vm = new BaseViewModel();
vm.Role = GetRoleNumber(User);
vm.MenuItems = contentService.GetMenuItems("00", vm.Role);
vm.Menu = pageService.GetMenu(vm.MenuItems, Request.FilePath);
// difference code here for each controller
}
All my controllers inherit from a controller called BaseController.
Is there a way I could move this code into my base controller and call it? If so then what would be the best way to implement this?
This is an exact candidate for the Repository Pattern.
You could create all of these in your Repository class and call that method in each ActionResult method
public void Repository : IRepository
{
public GetMyBaseViewModel()
{
//..implementation here
}
}
public interface IRepository
{
BaseViewModel GetMyBaseViewModel();
}
....
and in your controllers :
...
public class HomeController : Controller
{
//private repository member
private readonly IRepository _repository;
//controller constructors
//injecting the repository here
public HomeController() : this(new Repository())
{
}
public HomeController(IRepository repository)
{
_repository = repository;
}
//methods that call the repository for the vm data context
public ActionResult Index()
{
var vm = _repository.GetMyBaseViewModel();
return View();
}
}
You could make an abstract ActionResult method in your base controller:
protected BaseViewModel vm;
public ActionResult Index(string page, string title) {
vm = new BaseViewModel();
vm.Role = GetRoleNumber(User);
vm.MenuItems = contentService.GetMenuItems("00", vm.Role);
vm.Menu = pageService.GetMenu(vm.MenuItems, Request.FilePath);
try
{
return IndexSupplemental();
}
catch(NotImplementedException ex)
{
// Log and move on; the abstract method is not implemented.
}
return View();
}
protected abstract ActionResult IndexSupplemental();
Then every controller would have to implement this abstract method.
You can move it to a method in your base controller and call it when you need it.
public class BaseController : Controller
{
protected BaseViewModel _viewModel;
public void InitializeViewModel() {
vm = new BaseViewModel();
vm.Role = GetRoleNumber(User);
vm.MenuItems = contentService.GetMenuItems("00", vm.Role);
vm.Menu = pageService.GetMenu(vm.MenuItems, Request.FilePath);
}
}
An example:
public class MyController : BaseController
{
public ActionResult Index(string page, string title)
{
InitializeViewModel();
DoSomething(_viewModel);
}
}
In my projects most of my actions will return a viewmodel that inherits from the BaseViewModel but there are exceptions to this. So what I did was something like this in ControllerBase:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var authData = GetUserData();
if (authData != null)
{
var result = filterContext.Result as ViewResult;
if (result != null)
{
var vm = result.Model as ViewModelBase;
if (vm != null)
{
vm.UserId = authData.UserID;
vm.UserName = User.Identity.Name;
}
}
}
}
What you could do otherwise, as I expect your ViewModel to be of different types, is to create a method similar to this in ControllerBase:
NOTE This does not do what you want. I'm just showing a technique for creating a new instance of a derived class with some initialization code.
protected T Command<T>() where T : BaseCommand, new()
{
var command = new T();
command.IP = Request.UserHostAddress;
if (User != null && User.Identity.IsAuthenticated)
{
var authData = GetUserData();
if (authData != null)
{
command.UserId = authData.UserID;
}
}
return command;
}
Which would be used as
var command = Command<CreateUserCommand>();