MVC ASP.net session is null - asp.net-mvc

I have the following code which was ok until someone else put some other code in the site which sorta mucks it up now.
This is my code:
var existingContext = HttpContext.Current;
var writer = new StringWriter();
var response = new HttpResponse(writer);
var context = new HttpContext(existingContext.Request, response) { User = existingContext.User };
HttpContext.Current = context;
HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Default);
HttpContext.Current.Session["Test"] = "test";
for (Int32 i = 0; i < existingContext.Session.Count; i++)
{
HttpContext.Current.Session.Add(existingContext.Session.Keys[i], existingContext.Session[i]);
}
The idea behind this is to be able to capture the output of a view and render it to pdf. Now my only issue is that when i assign context back to HttpContext.Current, the session is null. I need to be able to initialize the session so that i can assign variables into it.
i will also add that this is inside a static class
public static class ControllerExtensions
Any clues?

If this is occurring inside of an HttpHandler, you need to add the IRequiresSessionState interface to your handler for the session to be available -
public class HttpPdfWriteHandler : IHttpHandler, IRequiresSessionState {
[...]
}
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.irequiressessionstate.aspx

I seem to have solved the issue for now and that was to remove the lines:
var context = new HttpContext(existingContext.Request, response) { User = existingContext.User };
HttpContext.Current = context;
HttpContext.Current.Request.
for (Int32 i = 0; i < existingContext.Session.Count; i++)
{
HttpContext.Current.Session.Add(existingContext.Session.Keys[i], existingContext.Session[i]);
}

1) Start–> Administrative Tools –> Services
2) right click over the ASP.NET State Service and click “start”
*additionally you could set the service to automatic so that it will work after a reboot.
For more details you can check my blog post: http://jamshidhashimi.com/2011/03/16/unable-to-make-the-session-state-request-to-the-session-state-server/
ref:
Unable to make the session state request to the session state server

Related

How do I get the type of the executing Controller in MVC .Net Core?

In my own DI I am trying to fork code based on whether the current request is executing in the context of an anoymous request or not. I guessed the easiest way would be to infer the type of the current controller and see if it was a subtype of our own anonymous api controller:
public bool InAnonymousContext() {
var anonymousContext = false;
if (_actionContextAccessor?.ActionContext != null)
{
var controllerContext = new ControllerContext(_actionContextAccessor.ActionContext);
var type = controllerContext.ActionDescriptor.ControllerTypeInfo?.GetType();
anonymousContext = type.IsSubclassOf(typeof(AbstractAnonymousApiController)) ?? false;
}
return anonymousContext;
}
but I've made a wrong assumption that IActionContextAccessor would be available to me from DI.
Any ideas how to go about this?
Found meta data on the HttpContext via DI & IHttpContextAccessor
var ctx = sp.GetRequiredService<IHttpContextAccessor>();
var action = ctx.HttpContext?.GetEndpoint()?.Metadata.GetMetadata<ControllerActionDescriptor>();
var type = action?.ControllerTypeInfo.AsType();
if (type != null && type.IsSubclassOf(typeof(AbstractAnonymousApiController)))
return null;

Identity Server 4 Multi-tenancy logout

I'm currently developing an identity server. It is multi-tenant with multiple user repositories.
I am able to pass (using Services.OpenIDConnect.Options) my tenant details from my MVC to the IDS in order to select the appropriate user repository on login
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("Tenant", "TenantDetail");
return Task.CompletedTask;
};
I am attempting to obtain this same information for logout, however the initial call to logout has some back end process that calls CustomProfileService.IsActiveAsync(IsActiveContext context).
I am unable to obtain the tenant information from the IsActiveContext, nor am i able to read any kind of query string (as i was using for login).
Any suggestions, or even alternative methods that might be more correct than what I'm attempting, would be greatly appreciated.
OnRedirectToIdentityProvider will not be hit on signout. You'll need to pass the tenant information in the OnRedirectToIdentityProviderForSignOut event in your client instead.
Here's a snippet, that's far from complete:
services
.AddOpenIdConnect("oidc", options =>
{
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProviderForSignOut = context =>
{
context.ProtocolMessage.AcrValues = "tenant:TenantDetail";
return Task.CompletedTask;
},
}
}
In IdentityServer you'll need to lookup the acr_values in the query parameters from the request. Inject IHttpContextAccessor in order to access the context:
public class ProfileService : IProfileService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ProfileService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// ...
}
public async Task IsActiveAsync(IsActiveContext context)
{
// Please note that this method is called on many occasions. Check context.Caller
// This means that you'll have to make sure that the acr_valus are present on all
// ocassions, hence the question in my comment.
var request = _httpContextAccessor.HttpContext.Request;
if (request.Method == HttpMethods.Get)
{
// acr_values should be present on all ocassions.
var values = (string)request.Query["acr_values"];
// This is just a sample, you'll need to parse the values.
var tenant = values.Split(':')[1];
}
// Your code where you link the repository ...
var sub = context.Subject.GetSubjectId();
var user = await userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
Please let me know if this solves the issue for you.

Run a URL string through the ASP.NET MVC pipeline to get an ActionResult

I have a list of URLs that I obtained by querying Google Analytics data. I want to run each of these URLs through the MVC pipeline to get the ActionResult. The action result contains the view model from which I can extract some important information.
Based on the extensibility of MVC, I thought this would be easy. I thought I could mock up a HttpRequest using the string URL and pass it through the routing and controller. My end point would be invoking the action method which would return the ActionResult. I'm finding bits and pieces of what I need, but a lot of the methods are protected within the various classes and the documentation on them is pretty sparse.
I somehow want to reach in to the ControllerActionInvoker and get the result of the call to the protected function InvokeActionMethod.
First of all, Darin's answer got me started, but there's a lot more detail to the final solution, so I'm adding a separate answer. This one is complex, so bear with me.
There are 4 steps to getting the ViewResult from a URL:
Mock the RequestContext via the routing system (Darin's answer got me started on this).
Uri uri = new Uri(MyStringUrl);
var request = new HttpRequest(null, uri.Scheme + "://" + uri.Authority + uri.AbsolutePath, string.IsNullOrWhiteSpace(uri.Query) ? null : uri.Query.Substring(1));
var response = new HttpResponse(new StringWriter());
var context = new HttpContext(request, response);
var contextBase = new HttpContextWrapper(context);
var routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(contextBase);
// We shouldn't have to do this, but the way we are mocking the request doesn't seem to pass the querystring data through to the route data.
foreach (string key in request.QueryString.Keys)
{
if (!routeData.Values.ContainsKey(key))
{
routeData.Values.Add(key, request.QueryString[key]);
}
}
var requestContext = new System.Web.Routing.RequestContext(contextBase, routeData);
Subclass your controller. Add a public method that allows you to call the protected Execute(RequestContext) method.
public void MyExecute(System.Web.Routing.RequestContext requestContext)
{
this.Execute(requestContext);
}
In the same subclassed controller, Add a public event that hooks in to the protected OnActionExecuted event. This allows you to reach in a grab the ViewResult via the ActionExecutedContext.
public delegate void MyActionExecutedHandler(ActionExecutedContext filterContext);
public event MyActionExecutedHandler MyActionExecuted;
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
if (MyActionExecuted != null)
{
MyActionExecuted(filterContext);
}
}
Tie everything together by instantiating an instance of the new controller subclass, adding an event handler, and calling the new public execute method (passing in the mocked RequestContext). The event handler will give you access to the ViewResult.
using (MyCompany.Controllers.MyController c = new Controllers.MyController())
{
c.MyActionExecuted += GrabActionResult;
try
{
c.MyExecute(requestContext);
}
catch (Exception)
{
// Handle an exception.
}
}
and here's the event handler:
private void GrabActionResult(System.Web.Mvc.ActionExecutedContext context)
{
if (context.Result.GetType() == typeof(ViewResult))
{
ViewResult result = context.Result as ViewResult;
}
else if (context.Result.GetType() == typeof(RedirectToRouteResult))
{
// Handle.
}
else if (context.Result.GetType() == typeof(HttpNotFoundResult))
{
// Handle.
}
else
{
// Handle.
}
}
The difficulty here consists into parsing the url into its constituent controller and action. Here's how this could be done:
var url = "http://example.com/Home/Index";
var request = new HttpRequest(null, url, "");
var response = new HttpResponse(new StringWriter.Null);
var context = new HttpContext(request, response);
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));
var values = routeData.Values;
var controller = values["controller"];
var action = values["action"];
Now that you know the controller and the action you could use reflection to instantiate and execute it.
Try this:
object result = null;
Type controller = Type.GetType("MvcApplication4.Controllers.HomeController");
if (controller != null)
{
object controllerObj = Activator.CreateInstance(controller, null);
if (controller.GetMethod("ActionName") != null)
{
result = ((ViewResult)controller.GetMethod("ActionName").Invoke(controllerObj, null)).ViewData.Model;
}
}
I assumed normal routes are configured in the application and can be retrieved using regex or string operations. Following your discussion, I learned that you guys want to really follow through the MVC pipeline by digging into the framework by not using reflection or any hardcording techniques. However, I tried to search to minimize hardcoding by trying to match the url with the routes configured in the application by following this thread
How to determine if an arbitrary URL matches a defined route
Also, I came across other thread which creates httprequest to access routedata object but again reflection needs to be used for this.
String URL to RouteValueDictionary
Thanks Ben Mills, this got me started with my own problem. However I found that I didn't have to do 2, 3 or 4, by doing the following.
Uri uri = new Uri(MyStringUrl);
var absoluteUri = uri.Scheme + "://" + uri.Authority + uri.AbsolutePath;
var query = string.IsNullOrWhiteSpace(uri.Query) ? null : uri.Query.Substring(1);
var request = new HttpRequest(null, absoluteUri, query);
Getting access to the string writer is important.
var sw = new StringWriter();
var response = new HttpResponse(sw);
var context = new HttpContext(request, response);
var contextBase = new HttpContextWrapper(context);
var routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(contextBase);
If we assign the RouteData to the request context we can use the MVC pipeline as intended.
request.RequestContext.RouteData = routeData;
var controllerName = routeData.GetRequiredString("controller");
var factory = ControllerBuilder.Current.GetControllerFactory();
var contoller = factory.CreateController(request.RequestContext, controllerName);
controller.Execute(request.RequestContext);
var viewResult = sw.ToString(); // this is our view result.
factory.ReleaseController(controller);
sw.Dispose();
I hope this helps someone else wanting to achieve similar things.

Set JsonSerializerSettings Per Response?

I have an MVC 4 Web API. Usually I want responses to return all properties, but there is one place I only want to return only non-null values. I can setup either behavior by setting the JsonSerializerSettings of the Formatters.JsonFormatter.SerializerSettings.NullValueHandling of the GlobalConfiguration.Configuration instance in the global file but I want to use both depending on the response. Is there an easy way to configure the request scope from within an API controller action?
By changing your controller action to return HttpResponseMessage you can get more control over how your content is returned for a particular action. e.g.
public HttpResponseMessage Get() {
var foo = new Foo();
var objectContent = new ObjectContent<Foo>(foo, new JsonFormatter()
{SerializerSettings.NullValueHandling = ???})
return new HttpResponseMessage() {Content = objectContent};
}
this would probably be easier to do on the client side with a dynamic language like javascript.
var keys = Object.keys(json);
for(var i = 0; i < keys.length; i++) {
var propertyName = keys[i];
if(json[propertyName] === undefined || v[propertyName] === null) {
json.remove(propertyName);
}
}
return json;

Merge an Object that wen outside the datacontext

I have the following question:
It is easy to insert an oBject in database with a form.
Just create an object
link it to the fields in your from.
Post back to controller,
create a new datacontext and do datacontext.InsertOnSubmit(object)
.
public static void AddPage(string lang, Page page)
{
using (var db = new CardReaderDataContext())
{
page.Lang = lang;
page.URL = UrlHelper.CreateValidSeoUrl(page.Name, "-");
db.Pages.InsertOnSubmit(page);
db.SubmitChanges();
}
}
But if you want to update an object, it is a tedious job.
You do the same flow,
you get the object,
link it to your form,
post it, but THEN !!!
because it went outside your datacontext, you have to reload the object from the datacontext,
transfer all the variables and save it,
this is a little complex explained so I give an example:
To update an object that you modified in a form:
public static void Update(Page page)
{
using (var db = new CardReaderDataContext())
{
var _page = db.Pages.Where(p => p.Guid == page.Guid).Single();
_page.ModificationDate = DateTime.Now;
_page.Title = page.Title;
_page.Description = page.Description;
_page.Content = page.Content;
_page.Keywords = page.Keywords;
_page.Name = page.Name;
_page.WTLang = page.WTLang;
_page.WTSKU = page.WTSKU;
_page.WTTi = page.WTTi;
_page.WTUri = page.WTUri;
_page.URL = UrlHelper.CreateValidSeoUrl(page.Name, "-");
// _page.Order = GetMaxOrderByMenuGuid(page.MenuGuid);
db.SubmitChanges();
}
}
I don't know if it is clear, if it isn't comment me, I will edit
I think you're looking for DataContext.Attach, but you can only use that with linqtosql objects that have been serialised/deserialised.
Have a read of the answer to this question -
http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/384a1c03-3acf-43ef-9a25-b84f93025e63/
"It's also not a good idea to even
attempt to fetch the old version. By
doing that you are in effect turning
off optimistic concurrency, so unless
you intended that this is a bad
approach. What you need to do is
round trip both the original state and
the current state of the object."

Resources