On my asp.net site I'm using an third party component for every user to provide data und subsume actions. Unfortunatly the component initialization is pretty slow (about 1-2 seconds) - way too long to initialize every page request.
So I'm trying to store this object in my application. But in any way it is null after the next page request. Simple objects are working as expected, of course.
I have tried so far
Static variables
HttpContext.Session
HttpContext.Cache
TempData
In all cases my application forgets everything, but I don't get any error.
Is there any way to achieve keeping those objects in memory?
Here is my minimal example. When I exchange the values to string everything works fine. With this object, I get null in Login method.
public class HomeController : Controller
{
public static IFrameworkItem Test;
// GET: Home
public ActionResult Index()
{
Test = FrameworkItem.Create("admin", "admin");
return View();
}
public ActionResult CreateItem(string username, string password)
{
var test = HomeController.Test;
return null;
}
}
Well, after many more hours of searching I found that this framework does logging on a file, which was not in App_Data. So I guess the ASP.NET website was restarted every time a file change was noticed.
A real bad issue, but now it is solved by forbidding this behavior.
Related
I am working on an asp.net core MVC project with PayPal integration. After completing the payment, the PayPal correctly redirect to the success URL (.../PayPal/PaymentSuccess). Please see the method given below:
PayPalController
public class PayPalController
{
public IActionResult PaymentSuccess()
{
//code for business logic here
TempData["Amount"] = amount;
return RedirectToAction("PaymentCompleted", "Home");
}
}
HomeController
public class HomeController
{
public IActionResult PaymentCompleted()
{
var amount = Convert.ToDecimal(TempData["Amount"]);
//code for business logic here
return View();
}
}
I have tried a payment using PayPal in the hosted environment.
After completing the payment the PayPal successfully invoked the PaymentSuccess() method. In this method, we have added the business logic for database updation. After this we need to redirect to another page "PaymentCompleted" to show the payment success message.
The issue is that the redirection to the view page "PaymentCompleted" which is in another controller (HomeController) is not working after successfully executing the code in PaymentSuccess method.
But, I have tried to invoke the PaymentSuccess() method directly in the browser and now the redirection is working.
EDIT: I have used a 'TempData' on 'PaymentSuccess()' method for passing paid amount to display on the 'PaymentCompleted' view page.
Why does this happen? Can anyone specify the reason behind this problem?
Any help would be appreciable. Thank You!
I had same with your issue before.
The problem the code handle business logic is exception and code runing can not reach RedirectToAction
You can add try/catch to handle exception, and log information to see detail
Finally, I got the solution. The issue is happened because of the way we used the TempData. We have to serialize the amount before assigning it to TempData in ASP.Net Core.
TempData["Amount"] = JsonConvert.SerializeObject(amount);
Also, we have to deserialize it before retrieving the TempData. No need to deserialize if it is string. I just showing the procedure. You can follow the below code:
var amount = JsonConvert.DeserializeObject<string>(TempData["Amount"].ToString());
But in MVC application, we don't need any serialization before assigning data to TempData.
Now the RedirectionToAction() method worked and I retrieved the paid amount on the PaymentCompleted() method using TempData.
I'm currently working on a webserver in asp.net core.
I want the server to process the users input and data and am looking for a good solution to save complex Objects for the runtime.
So my first approach was to use Sessions. In Asp.net, sessions used to work like Session["key"] = new ValueObject()
In asp.net core however you can only use the methods SetString, SetInt32 and Set for byte arrays. I found a lot of solutions which basically converted the objects into Json strings. However in my case this isn't possible due to the objects containing other object references and more.
My second idea was to create a list of objects with the SessionId as identifier. Problem with this is that every time I would make request to the server, it needs to go through all existing Sessions to find the matching one, so this would probably drastically increase the time for the request.
So my question is what would be the best way to save user related objects?
Is using Sessions even the best way for solving this problem or am I missing something?
Note: Request are handled by JQuery AJAX, so reloading the page for accessing data is not an option.
You could try using the MemoryCache that can hold any .net type. It is not a problem but given it is a shared structure, it will be shared to all users, so, you have to carefull manage it. To do it, you could use HttpContext.Session.Id to define the keys on the memory cache instance. For sample (pseudo-code I didn't test):
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
public async Task<IActionResult> CacheGetOrCreateAsynchronous()
{
string cacheKey = $"{HttpContext.Session.Id}_data";
var cacheEntry = await
_cache.GetOrCreateAsync(cacheKey , entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3);
return Task.FromResult(DateTime.Now);
});
return View("Cache", cacheEntry);
}
}
Im new to asp.net core and I was trying to develop an online SQL database manager, that will work on any SQL database, after passing: ServerAddress, Login, Password and DatabaseType (my ConnectionInformation model).
Something like SSMS but online.
I want to pass my ConnectionInformation model from Login controller to Database controller.
Redirecting to action uses query string which exposes all of my data.
TempData only accepts strings and converting my model to json isn't the most elegant way to solve this problem.
Login Controller:
public class LoginController : Controller
{
private readonly ILoginLogic _loginLogic;
public LoginController(ILoginLogic loginLogic)
{
_loginLogic = loginLogic;
}
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(ConnectionInformationViewModel connectionViewModel)
{
if (!ModelState.IsValid)
return View();
ConnectionInformation connection = Mapper.Mapper.ConnectionInformationMapper(connectionViewModel);
var connectionSuccess = _loginLogic.ConnectToDatabase(connection);
if (connectionSuccess)
return RedirectToAction("Index", "Database", connection);
else
return View(); // TODO: Return view with error or handle it in js
}
}
Database Controller:
public class DatabaseController : Controller
{
private readonly IDatabaseLogic _databaseLogic;
public DatabaseController(IDatabaseLogic databaseLogic)
{
_databaseLogic = databaseLogic;
}
public IActionResult Index(ConnectionInformation connection)
{
var databases = _databaseLogic.GetDatabases(connection);
return View(databases);
}
}
Prehaps my approach is totally wrong. My main goal is to check if I can connect to database, and if I can I want to perform query operations on that Database untill user logs out.
Well, simply, you have to persist the information some way. In that regard, there's a number of options. You could persist it in local storage on the client and actually pass it back with each further request. That works better for SPA-style apps, where you're pretty much doing everything via AJAX, though. Another technically client-side storage mechanism would be setting a session cookie with the posted connection information. Here I'm talking about explicitly setting and reading from a cookie with a "session" lifetime, not using sessions.
Or you can can actually use a true session, i.e. Session. TempData is basically just Session anyways, but here it would be inappropriate as you'd then need to ensure that the TempData is kept every time it's accessed or it won't survive the next request. If you're doing that, then you might as well just use Session and not have to worry about it.
Serialization is pretty much required no matter what you do. There's no way to persist an actual C# object instance, so you're either going to have to write it to a relational store like a database or serialize it to JSON.
One alternate solution, which avoids having to persist the actual connection information is to basically create your own connection pool. This will require a singleton-scoped class with a ConcurrentDictionary ivar and likely the use of SemaphoreSlim to lock during reads and writes of that dictionary so that you don't create and orphan connections. Then, you'd just need to assign the key to their particular connection in the dictionary client somehow, such as via Session or a cookie. This is actually a little more secure as well, as you're not persisting the database connection info past the initial post, but you might end up exhausting the available server connections if there's too many simultaneous users. Of course, that could potentially be an issue regardless. You'll also need some policy for eviction of connections. It's not the easiest setup.
Personally, I'd just stick with using Session on this one. It's secure enough, as long as you take the standard session hijacking prevention measures, and it's simple to implement. If you are building a SPA-style app, then I'd stick with local storage, as that's going to be imminently better keeping the info client-side to begin with, but it requires a bit more plumbing that way.
I have a ASP.NET Core application where I have to collect a handful of information about an applicant. One of the fields is SSN.
I have a GET action in my controller that displays the fields of the form. It looks like this:
[HttpGet]
[Route("~/applicant/")]
public IActionResult Applicant(int id, Guid guid)
{
var model = CreateEmptyViewModel();
return View(model);
}
I then have a POST action in my controller that checks if the form submission is valid and moves on or reloads the form accordingly.
[HttpPost]
[Route("~/post-applicant")]
public IActionResult PostApplicant(MyViewModel model)
{
if (model == null) throw new ArgumentNullException(nameof(model));
if (ModelState.IsValid)
{
// code that moves on
}
else
{
TempData["Error"] = "The form is incomplete or corrections are needed.";
return View(nameof(Applicant), model); // reloads form with fields filled out
}
}
My view model looks like this:
public class MyViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string SSN { get; set; }
}
All properties in MyViewModel are required. If the user decides to supply SSN but not first name, the form submission will fail and the form will be reloaded.
Are there any security related ramifications for reloading a form with recently typed sensitive information? Is there a better way to do what I am doing?
Encrypting your requests and response via SSL is good first step, but it is not a solution in itself. HTTPS traffic can be intercepted and man-in-the-middle attacks are a real concern, particular on insecure networks like public wifi. Unless you can ensure that all your users are running latest and greatest platforms with all security patches applied and are always connected to secure networks directly or via a secure VPN, then you can't just brush off PII as fine because it's HTTPS. Even then, there's always zero-day exploits you can never account for.
Long and short, you should treat all sensitive PII as sacrosanct at all times and never transfer it over the line unless you have to. Initial collection is one such occasion, but that doesn't mean it should come back over the line on error. It's perfectly okay to make a user re-enter sensitive information again, and most users tend to understand why they have to. For example, if you make an error with a credit card payment form, your credit card number doesn't come back filled in already - that would be a severe violation of PCI.
So, in your controller action, you should do the following:
ModelState.Remove("SSN");
model.SSN = null;
return View(model);
That said, there's probably worse PII to potentially leak at this point. Thanks to Equifax, virtually everyone's SSN is already public. Still, it's always good to think about what data you're sending back and forth.
As long as you are using HTTPS, then there are no security implications.
The only way you could make it better is by doing the POST as an AJAX request in the background. Then you only need to return the error message if there is one. The only real benefit is a better user experience since they don't need to wait for a full page refresh. There's an example of that here: https://stackoverflow.com/a/6960586/1202807
But the way you're doing it is fine too.
Hi I cant find the way to pass data between Action methods within one controller. The simplified code example is as follows:
public class HomeController : Controller
{
int latestID;
// GET: Home
public ActionResult Index()
{
latestID = 50000;
return View();
}
// GET: Generate
public ActionResult Generate()
{
// using the latestID here
return View();
}
}
Index loads when the app starts and latestID is set to 50000. But when I click button mapped to API GET request Generate, the latestID is suddenly null. I tried to implement TempData and Session but with no luck. Any help would be greatly appreciated!
I feel there are 2 possibilities here i.e. the value to be retrieved is for a user session or its global across all the users using the website.
If the value is user specific then it can be managed through session stage management like Viewbag. Or if this value is expected to be maintained on the server side alone then it needs to be retrieved again through some persistence mechanism like database or memory cache.
If the value is common across all the users using the Website then it can be achieved through the Dependency Injection of a singleton object (this can be a database manager or cache manager again or a simple in memory object). The static objects can be used as well but it wouldn't be ideal as the application wouldn't support horizontal scalability across instances.