Using MVC
Naturally, after authentication in a Web API, I want to assign session("LoggedIn") the value True.
But the session in my Web API keeps returning NullReference.
Any workaround?
Thanks.
WebApiConfig.cs
public static class WebApiConfig
{
public static string UrlPrefix { get { return "api"; } }
public static string UrlPrefixRelative { get { return "~/api"; } }
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
...
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
private bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);
}
}
for more details Accessing Session Using ASP.NET Web API
You need to Sets the type of session state behavior that is required in order to support an HTTP request.
You can do the changes in Application_PostAuthorizeRequest() in Global.asax file.
using System.Web.Http;
using System.Web;
using System.Web.SessionState;
namespace Sample
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
WebApiConfig.Register(GlobalConfiguration.Configuration);
}
protected void Application_PostAuthorizeRequest()
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
}
Related
In MVC 5 you could assign a value to session in global.asx when the session started. Is there a way you can do this in .Net Core MVC? I have session configured but in the middleware it seems to get called on every request.
nercan's solution will work, but I think I found a solution that requires less code and may have other advantages.
First, wrap DistributedSessionStore like this:
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Session;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
public interface IStartSession
{
void StartSession(ISession session);
}
public class DistributedSessionStoreWithStart : ISessionStore
{
DistributedSessionStore innerStore;
IStartSession startSession;
public DistributedSessionStoreWithStart(IDistributedCache cache,
ILoggerFactory loggerFactory, IStartSession startSession)
{
innerStore = new DistributedSessionStore(cache, loggerFactory);
this.startSession = startSession;
}
public ISession Create(string sessionKey, TimeSpan idleTimeout,
TimeSpan ioTimeout, Func<bool> tryEstablishSession,
bool isNewSessionKey)
{
ISession session = innerStore.Create(sessionKey, idleTimeout, ioTimeout,
tryEstablishSession, isNewSessionKey);
if (isNewSessionKey)
{
startSession.StartSession(session);
}
return session;
}
}
Then register this new class in Startup.cs:
class InitSession : IStartSession
{
public void StartSession(ISession session)
{
session.SetString("Hello", "World");
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<IStartSession, InitSession>();
services.AddSingleton<ISessionStore, DistributedSessionStoreWithStart>();
services.AddSession();
...
}
Full code is here:
https://github.com/SurferJeffAtGoogle/scratch/tree/master/StartSession/MVC
I use it in a live project. It works correctly. if you want to keep it when the application stops. You should use DistributedCache. For example, I'm using DistributedRedisCache.
Add to startup this code;
public void ConfigureServices(IServiceCollection services)
{
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(60);
options.Cookie.HttpOnly = true;
});
// for redis distributed cache
//services.AddDistributedRedisCache(options =>
// {
// options.InstanceName = $"{Configuration["DistributedRedisCacheInstance"]}";
// options.Configuration = $"{Configuration["DistributedRedisCacheHost"]}";
// });
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IHttpContextAccessor acc)
{
app.UseSession();
}
And add new session extension;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System.Text;
namespace SampleApp
{
public static class SessionExtensions
{
public static void SetObjectAsJson<T>(this ISession session, string key, T value)
{
session.Set(key, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)));
}
public static T GetObjectFromJson<T>(this ISession session, string key)
{
session.TryGetValue(key, out byte[] dataByte);
string data = dataByte != null ? Encoding.UTF8.GetString(dataByte) : null;
return data == null ? default(T) : JsonConvert.DeserializeObject<T>(data);
}
}
}
And use get or set same this;
var sessionItem = httpContext.Session.GetObjectFromJson<string>("sessionItem");
//or
ContextProviderExtension.HttpContextAccessor.HttpContext.Session.SetObjectAsJson("sessionItem", sessionItem);
you need this extension;
using Microsoft.AspNetCore.Http;
using System;
namespace SampleApp
{
public static class ContextProviderExtension
{
static IHttpContextAccessor httpContextAccessor = null;
public static IHttpContextAccessor HttpContextAccessor
{
get { return httpContextAccessor; }
set
{
if (httpContextAccessor != null)
{
throw new Exception("");
}
httpContextAccessor = value;
}
}
}
}
I suppose it will work.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace SampleApp
{
public class SessionMiddleware
{
private readonly RequestDelegate _next;
public SessionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
var sessionItem = httpContext.Session.GetObjectFromJson<string>("test");
if (sessionItem == null)
httpContext.Session.SetObjectAsJson<string>("test", httpContext.Session.Id);//httpContext.Session.Id or set a value
await _next.Invoke(httpContext);
}
}
public static class SessionMiddlewareExtensions
{
public static IApplicationBuilder UseSessionMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<SessionMiddleware>();
}
}
}
and add startup.cs Configure method after app.UseSession();
app.UseSessionMiddleware();
There are lots of link I found for reference. But unfortunately they didn't worked for me. So I am posting here. Issue is my control is not hitting the "OnActionExecuting". This is the first time I am working with WebAPI and I guess I am missing something somewhere. Please HELP!!
My API filter looks like this:
public class ValidateCustomAntiForgeryTokenAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public new void OnActionExecuted(HttpActionExecutedContext filterContext)
{
//removed for Breviety
}
public void OnActionExecuting(HttpActionExecutedContext request)
{
//removed for Breviety
}
}
I registered my filter on Startup.cs:
public class StartUp : System.Web.HttpApplication
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
//removed for Breveity
config.Filters.Add(new ValidateCustomAntiForgeryTokenAttribute());
}
}
And using it on one of my controller:
[CustomAuthorize]
[ValidateCustomAntiForgeryToken]
public class QuestionController : ApiController
{
}
public void OnActionExecuting(HttpActionContext request)
{
//removed for Breviety
}
OnActionExecuting expects parameter HttpActionContext
i want to access action result in controller(my controlelr is HotelController action is Index)
(http://localhost:9001/Hotel/Index) it gives below error
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Hotel/Index
Hotel controller
public class HotelController : Base.BoxyController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
ViewBag.Title = "SonDakka - Otel";
}
public ActionResult Index(string culture)
{
.........
BoxyController
public class BoxyController : MainController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
..........
MainController
public class MainController : SiteController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
......
SiteController
[ExitHttpsIfNotRequired]
public class SiteController : Controller
{
public Account Me { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
.......
and this is my global.asax
using System;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using Tourism.Data;
using Tourism.Data.Mvc.Authorization;
using Tourism.Data.Mvc.Routing;
namespace Tourism
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(TourismContext db, RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var cultures = db.Cultures.Select(c => c.Code).ToArray();
routes.MapRoute
(
"Ajax",
"{culture}/{controller}/{action}/{id}",
new { id = UrlParameter.Optional },
new { culture = new ArrayRouteConstraint(true, cultures), controller = new ArrayRouteConstraint(true, "Ajax") }
).RouteHandler = new GlobalizedRouteHandler();
routes.Add
(
"Page",
new GlobalizedPageRoute
(
"{culture}/{path}",
null,
new RouteValueDictionary { { "culture", new ArrayRouteConstraint(true, cultures) } },
new GlobalizedRouteHandler()
)
);
routes.Add
(
"Route",
new GlobalizedRoute
(
"{culture}/{path}/{slug}/{id}",
new RouteValueDictionary { { "culture", UrlParameter.Optional }, { "path", UrlParameter.Optional }, { "slug", UrlParameter.Optional }, { "id", UrlParameter.Optional } },
new RouteValueDictionary { { "culture", new ArrayRouteConstraint(false, cultures) } },
new GlobalizedRouteHandler()
)
);
}
protected void Application_Start()
{
Database.SetInitializer<TourismContext>(null);
using (var db = new TourismContext())
{
#if !DEBUG
if (!db.Database.CompatibleWithModel(true))
{
System.Web.HttpRuntime.UnloadAppDomain();
throw new Exception("Veritabanı değişikliği tespit edildi.");
}
#endif
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(db, RouteTable.Routes);
}
}
protected void Application_PostAuthenticateRequest()
{
if (Request.IsAuthenticated)
{
Context.User = System.Threading.Thread.CurrentPrincipal =
new AuthorizationPrincipal(Context.User.Identity);
}
}
}
}
Because this is due to Razor engine unable to find Thanks action in Hotel controller. You need to make a Thanks action with in Hotel controller like this:
public class HotelController : Base.BoxyController
{
public ActionResult Thanks(string culture)
{
return View();
}
}
And also make sure to create a view in Hotel folder with your html code.
Based on the route config you posted, your URL should be with culture, for example:
http://localhost:9001/en/Hotel/Index
Notice the en before Hotel. It could be any value that is valid in your database.
I'm trying to learn a bit about MVC and have come across a problem when using Ninject. I want to bind repositories but keep getting the 'Object reference not set to an instance of an object' error.
I have created my NinjectControllerFactory:
public class NinjectControllerFactory : DefaultControllerFactory
{
// A Ninject "kernel" is the thing that can supply object instances
private IKernel kernel = new StandardKernel(new SportsShopServices());
// ASP .NET MVC calls this to get the controller for each request
protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
if (controllerType == null)
return null;
return (IController) kernel.Get(controllerType);
}
// Configure how abstract sevice types are mapped to concrete implementations
private class SportsShopServices : NinjectModule
{
public override void Load()
{
Bind<IProductRepository>().To<SqlProductsRepository>()
.WithConstructorArgument("connectionString",
ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString);
}
}
}
and my controller :
public class ProductsController : Controller
{
private IProductRepository productsRepository;
// Constructor used with Ninject
public ProductsController(IProductRepository _productsRepository)
{
this.productsRepository = _productsRepository;
}
public ViewResult List()
{
return View(productsRepository.Products.ToList());
}
}
I have modified the Web.config file to provide the db connection string and the Global.asax file Application_Start() method to include:
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
I am working on an example from the PRO ASP .NET MVC 2 book but just can't get this work, been trying all day.
If you just want out-out-the-box ninject functionality, you are doing too much by creating your own controller factory.
all you need is the following in global.asax
public class MvcApplication : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new ServiceModule()
};
return new StandardKernel(modules);
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
RegisterAllControllersIn(Assembly.GetExecutingAssembly());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
internal class ServiceModule : NinjectModule
{
public override void Load()
{
// controllers
this.Bind<Controllers.AccountController>().ToSelf();
this.Bind<Controllers.HomeController>().ToSelf();
// Repository
Bind<Controllers.IFormsAuthentication>().To<Controllers.FormsAuthenticationService>();
Bind<Controllers.IMembershipService>().To<Controllers.AccountMembershipService>();
}
}
}
Edit
Orignal Title: My transaction is closed by the time it gets to my Repo. What am I doing wrong?
I got a answer to my origanl questions(I forgot to open the transaction lol). Now I am wondering if my code is automatically closing the session or if I have to somehow tell it to do this.
Hi
I am using mvc 3.0, nhibernate, fluent nhibernate and ninject 2.0
Global.asax
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
// Hook our DI stuff when application starts
SetupDependencyInjection();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
public void SetupDependencyInjection()
{
// Tell ASP.NET MVC 3 to use our Ninject DI Container
DependencyResolver.SetResolver(new NinjectDependencyResolver(CreateKernel()));
}
protected IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new NhibernateModule(),
new ServiceModule(),
new RepoModule()
};
return new StandardKernel(modules);
}
}
Session Factory
public class NhibernateSessionFactory
{
public ISessionFactory GetSessionFactory()
{
ISessionFactory fluentConfiguration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("test")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MyMaps>())
.BuildSessionFactory();
return fluentConfiguration;
}
}
Session Factory Provider
public class NhibernateSessionFactoryProvider : Provider<ISessionFactory>
{
protected override ISessionFactory CreateInstance(IContext context)
{
var sessionFactory = new NhibernateSessionFactory();
return sessionFactory.GetSessionFactory();
}
}
Nhibernate Module
public class NhibernateModule : NinjectModule
{
public override void Load()
{
Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
}
}
Service Module
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ITest>().To<Test>();
}
}
Repo Module
public class RepoModule : NinjectModule
{
public override void Load()
{
Bind<IStudentRepo>().To<StudentRepo>();
}
}
HomeController
private readonly ITest test;
public HomeController(ITest test)
{
this.test = test;
}
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
Test(my service layer file)
public class Test : ITest
{
private readonly IStudentRepo studentRepo;
public Test(IStudentRepo studentRepo)
{
this.studentRepo = studentRepo;
}
}
Repo
public class StudentRepo : IStudentRepo
{
private readonly ISession session;
public StudentRepo(ISession session)
{
this.session = session;
}
}
When I look through my debugger at the session that is coming into my repo. It says the session is open and connected but the (session.Transaction).IsActive = false
You're currently set up to use implicit transactions, which I don't believe are exposed through session.Transaction. Of course, Use of implicit transactions is discouraged.