This is essentially the same question as this one:
ASP.NET MVC - Preserve POST data after Authorize attribute login redirect except it isn't asked 7 years ago, and it's about ASP.NET Core, which is pretty different. I am using the [Authorize] attribute to do my most basic access authentication, really just to check to see if there is a user logged in at all. If not, then it kicks them back to the login page. Here's the services setup for that.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "loginId";
});
services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/Logout");
Here is my Logout action.
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Logout(string returnUrl = "/cgi-s/admin.cgi")
{
await HttpContext.SignOutAsync();
if (HttpContext.Request.Cookies.TryGetValue("loginId", out string loginId))
{
User auth = _context.Users.Where(a => a.LoginId.Equals(loginId)).FirstOrDefault();
if (auth != null)
{
auth.LoginId = string.Empty;
await _context.SaveChangesAsync();
}
}
return Redirect("/Account/Login?returnUrl="+returnUrl);
}
Right now I am just using the default behavior with a return url string to get back to the attempted page after a successful login. Is there a way to set this up so that POST data is also preserved?
I've tried a couple different things, like a custom middleware that stores post data which then gets retrieved on login, but I haven't come up with anything that I haven't found security holes in afterward.
Is there an easy way to do this that I'm just missing?
Thanks.
PS. Please ignore the weirdness going on in the Logout action. We are a two man team working on a 20 year old Perl CGI site, slowly transitioning over to .NET while trying to also keep up with new features and bug fixes, so everything is weird while we run Perl CGI alongside some .NET code on IIS with Postgres. Hopefully we will eventually get everything transitioned over.
Have you tried httpContext.Request.EnableBuffering?
See this question here and be sure to look at the comments: Read the body of a request as string inside middleware
httpContext.Request.EnableBuffering();
using(StreamReader streamReader = new StreamReader(httpContext.Request.Body,
...,leaveOpen: true))
{
//Do something here:
httpContext.Request.Body.Position = 0;
}
I have a dot net mvc application and want to perform a database check about whether the user has accepted the terms and conditions or not at the start of the application and redirect the user to the terms and condition page based on the result. Where should I place the code snippet ?
Till now I tried to redirect from global.asax file to a route and then calling a method from the control to perform the check But however it is giving the Response does not exist in the currrent context.
I tried this piece of code :
Response.RedirectToRoute("Terms",false);
I am very new to this so please excuse if the question really dumb.
Place your code snippet in the ActionResult of the Index Page(which is the default page that loads when the application starts.) in the HomeController.
Get your user from either a session and check against the user in your database to see if they have read the terms and conditions.
It will look something like this
public ActionResult Index()
{
var user = Session["loggedUser"] as User;
user = db.Users.Find(user.id);
if(!user.hasReadTerms)
{
return Redirect("/Terms");
}else
{
//continue
}
}
Currently, my application can track whenever user has logged in or out. The logging out activity is saved to database because User simply click a button. However, I would be interested in how to implement something similar but when user's ticket is no longer valid, either because it's been removed or expired.
I would like to handle any event (if possible) that is fired whenever user is no longer valid.
Is it possible to implement anything like this?
Cheers!
EDIT
I found two solutions. One uses BaseController--the code below from Handle asp.net MVC session expiration. This is simply what the author claims the easiest and working solution. First, is it a good idea to inherit all conrollers from a custom controllers? Second, I have already customized my own [Authorize] attribute. I don't know how all this together is going to work, and three, can i still have access to the cookie of a logged user where I hold UserID and some other data such as UserRole? And also it is not triggered automatically but whenver user trying to access and cookie is expired. It could be 6 hours later.
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Check if session exists
if (filterContext.HttpContext.Session != null)
{
//if new session
if (filterContext.HttpContext.Session.IsNewSession)
{
string cookie = filterContext.HttpContext.Request.Headers["Cookie"];
//if cookie exists and session id index is greater than zero
if ((cookie != null) && (cookie.IndexOf("ASP.NET_SessionId") >= 0))
{
//redirect to desired session expiration action and controller
filterContext.Result = RedirectToAction("SessionExpired", "Home");
return;
}
}
}
//otherwise continue with action
base.OnActionExecuting(filterContext);
}
}
The second solution I found is based on Global.aspx's function
void Session_End(object sender, EventArgs e)
{
Response.Redirect("~/Account/Timeout");
}
but I read it only works when I use InProc. I don't know what that is, though.
If you go with the filter [Authorize] you can add it as a global filter.
filters.Add(new AuthorizeAttribute());
Which is what your controller does too.
To answer your questions:
Yes, if you are building a framework that impacts all controllers. There is no problem with inheriting from a base controller.
Looks like the global filter is right up your alley.
You can't access the cookie once the session expires since there is no client!
When a session expires, the screen gets frozen to information they've already been authorized for until any subsequent request is made.
Hope that helps.
This question already has answers here:
Is it possible to make an ASP.NET MVC route based on a subdomain?
(10 answers)
Closed 3 years ago.
I want to use routing for all incomming calls to my server and then only use the routing on some of these (depending on values in my database).
I know how routing is done, what I don't know is how to abort routing inside my IRouteHandler.
ex
if (routing)
{
// do routing
var page = BuildManager.CreateInstanceFromVirtualPath(VirtualPath, typeof(IHttpHandler)) as IHttpHandler;
return page;
}
else
{
// proceed as if no routing has been made
}
Respone.Redirect()
Then you redirect them to a error or default page.
I'd like to take data entered in an MVC user form and display it in a different view.
The class has the following private variable:
IList<string> _pagecontent = new List<string>();
The following action accepts a FormCollection object, validates it, and passes it on to the "Preview" view as a List:
[Authorize(Roles = "Admins")]
[ValidateInput(false)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UpdateContent(FormCollection collection)
{
if (ModelState.IsValid)
{
string PageToInsert = collection["PageToInsert"];
string PageHeader = collection["PageHeader"];
string PageBody = collection["PageBody"];
//validate, excluded...
_pagecontent.Add(PageToInsert);
_pagecontent.Add(PageHeader);
_pagecontent.Add(PageBody);
}
return RedirectToAction("Preview", _pagecontent);
}
The Preview view has the following Page Directive for passing a strongly typed object List:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Template.Master" Inherits="System.Web.Mvc.ViewPage<List<string>>" %>
I would expect to be able to use the Model object to get my data, but alas I cannot. At the following line, I get an error index out of bounds exception, stating the index must be non-negative and less than the size of the collection:
<% if (Model[0].ToString() == "0") { %>
And some strange parameters have been added to the URL, as it resolves to
http://localhost:1894/Admin/Preview?Capacity=4&Count=3
So I have two questions:
When I call RedirectToAction and pass it my List, why is it inaccessible in the view's Model object?
What is the proper way to go about doing what I'm trying to do, namely pass a collection of strings to a view for display there?
Try using TempData. It is like a single-shot session object. You put values you want into TempData, immediately redirect and get them out. There is a good writeup here: http://blogs.teamb.com/craigstuntz/2009/01/23/37947/
Be careful when using TempData. It works great in a single server environment but in a cloud environment it may not work as expected since you cannot guarantee that the request will hit the same machine. This happens because TempData rely on the asp.net session. But if you are using other session manager like SQL or AppFabric Cache it will work fine.
The second parameter to RedirectAction is routeValues, not model.
protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues);
Try using TempData for the model. Its for persisting data between redirects.
The problem with RedirectToAction is it's returning a HTTP 302 and the browser is then on it's own going and doing a brand new HTTP request. You may want to consider using a cookie and/or session object to persist the data between requests.
This is not working because RedirectToAction is actually sending back a Http 302 to the browser. When the browser receives this 302, it does a new request to the server asking for the new page. New request, new temp variables.
You will also face this problem when you try to save/edit/delete something and for some reason you deny it and you have to return the old form again.
So, instead of:
return RedirectToAction("Preview", _pagecontent);
Put the Preview logic in a separate method and just call it:
return PreviewLogic(_pagecontent);
You can also use the TempData[] dic to persist data for the next request like others have said, but then you will not avoid the 302 additional round trip to the server.
It sounds like you're trying to do:
public ActionResult UpdateContent(FormCollection form) {
...
return View("Preview", _pagecontent);
}
Note that a redirection is supposed to be a "clean slate" for the browser (except for things like the auth cookie). You don't get to tell the browser to pass information along to the next request, since the next request should be able to stand on its own. All you get to do is tell the browser what URL to request next. In ASP.NET MVC, when you pass an arguments-object to RedirectToAction, the public properties of that object are appended as query-string parameters to the generated URL.
Can't you just make 2 action results with the same name and mark 1 of them with HttpPost?
public ActionResult UpdateContent(FormCollection preview = null)
{
return View(preview);
}
[HttpPost]
public ActionResult UpdateContent(FormCollection collection = null, bool preview = false)
{
if (preview)
return UpdateContent(collection);
else
return UpdateContent(null);
}
It looks like you are looking for the UpdateModel command:
Check out ScottGu's blog post on the topic:
Improved UpdateModel and TryUpdateModel methods