I'm using a Webservice coded in C# using .Net Framework 3.5.
The WebMethods of this Webservice besides their main workload also connect to Salesforce to retrieve and write data, so I'm using a static variable for keeping the Salesforce api login object alive between requests, this way I only log in once (on Salesforce) and then reuse this variable for subsequent calls, avoiding the need to login on each request.
This static object is evaluated on the main ctor of the Webservice, to check if some properties inside the login object are valid (for example, the validity of the session), if not, the login method is called again.
This works with no problems at all on my development environment (Windows 7 and VS2012 IIS7.5? Dev server) and in a test server (Win Server 2003, IIS6), but it doesn't work at all on the production box which is also a Windows Server 2003 with IIS6, because the static variable value is null on each request, logging in Salesforce on every request, giving long response times for each call and also hitting some limits on Salesforce, locking the account and blocking any following logins.
Sadly both machines (test and production) don't have the same configuration and the production box is currently unreachable for me, so the recycling time of the App Pool and other specifics are unknown to me at the moment.
I think this is a configuration issue, but anyway here is my code, firstly i started only by having a static variable inside the Webservice main class and then (current version) creating a whole static class with static variables.
All of the following code is under the same namespace
Static class (Salesforce login logic):
public static class Srv
{
public static SFHelpers helper = new SFHelpers(); // own class, Holds Salesforce logic and data related to this requirement
public static SforceService SFserv = new SforceService(); // Salesforce class that handles login (and other Salesforce data manipulation methods)
public static DateTime NextLoginSF = DateTime.MinValue; // Determines when does the Salesforce session expires
public static void LoginSalesforce()
{ // Simplified salesforce login steps, removed try-catch and other conditions to facilitate comprehension
SFserv.Url = helper.URLSalesforce;
LoginResult loginResult = SFserv.login(SFuser, SFpass);
NextLoginSF = DateTime.Now.AddSeconds(loginResult.userInfo.sessionSecondsValid);
SFserv.Url = loginResult.serverUrl;
SFserv.SessionHeaderValue = new SessionHeader { sessionId = loginResult.sessionId };
}
}
Main ctor and sample Webmethod:
[WebService(Namespace = "http://helloSO.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WsSFTest : System.Web.Services.WebService
{
public WsSFTest()
{
try
{
if (Srv.SFserv.SessionHeaderValue == null || DateTime.Now >= Srv.NextLoginSF) // any of this will trigger a login to renew Salesforce session
{
Srv.LoginSalesforce();
}
}
catch (SoapException se)
{
if (se.Code.Name == ExceptionCode.INVALID_SESSION_ID.ToString())
{ // Login again because Salesforce invalidated my session somehow
Srv.LoginSalesforce();
}
else
{
// Irrelevant
}
}
catch (Exception e)
{
// Irrelevant
}
}
[WebMethod]
public SampleResult SampleMethod(int param)
{
try
{
//irrelevant code gathers values here
var something = Srv.helper.Method(param, anotherParam);
return something;
}
catch (Exception e)
{
// Irrelevant
}
}
}
Serializing the login object to a local file or persistance database are my last options since quick response times are a must.
I think what's left for me to try are Session variables but given this behavior odds that something similar could happen seem high.
Any clues? Thanks in advance
At the end I realized some evaluation value in this line (Specifically Srv.NextLoginSF)
if (Srv.SFserv.SessionHeaderValue == null || DateTime.Now >= Srv.NextLoginSF) // any of this will trigger a login to renew Salesforce session
Was returning with a different value than expected, and that the static variable wasn't losing its value
Related
I took over a project is ASP.NET, C# and React which is not too well documented. In Visual Studio (Professional 2017)'s debug mode, I initially keep getting a bunch of errors which I ignore by clicking Continue a couple of times. The Output within Visual Studio reads:
An exception of type 'System.Web.Http.HttpResponseException' occurred in myProject.API.dll but was not handled in user code
Processing of the HTTP request resulted in an exception.
Please see the HTTP response returned by the 'Response' property of this exception for details.
The corresponding code is probably myProject.BSTB.API\Filters\UserAuthenticationFilter.cs:
namespace myProject.API.Filters
{
public class UserAuthenticationFilter : ActionFilterAttribute
{
// ... some other code
public override void OnActionExecuting(HttpActionContext actionContext)
{
var name = HttpContext.Current.User.Identity.Name;
ServiceLocator sl = new ServiceLocator();
User user = null;
try { user = sl.User.GetUserByName(name); } catch (Exception ex) { throw; }
if (user == null)
{
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.Unauthorized) {
ReasonPhrase = "Unauthorize request: User not valid: " + name});
}
HttpContext.Current.Items.Add(Common.CURRENT_CONTEXT_USER, user);
base.OnActionExecuting(actionContext);
}
}
}
I was told:
Since the system uses NTLM authentication and credentials are not sent on the first call to the server, the server sends an error response, and the credentials are sent with the second request. Thus it is expected that the server will have user == null towards the beginning, and spit out a lot of HTTP errors, this is the desired behaviour.
Edit: I believe that we are actually rather using Kerberos and we actually performing an authorization rather than an authentication, see e.g. Authentication versus Authorization.
Interestingly, other developers cannot reproduce this issue, so it might have to do something with the way I set up Visual Studio.
Clearly it is a waste of time that I have to click several times on the Continue button each time when I start running the code in Visual Studio. How should I adjust the code that this error does not show up any more? Is there maybe just a configuration in Visual Studio or some additional code I should add?
Edit
The user comes from an additional service my.Service\UserService.cs which reads
namespace myProject.Service
{
public class UserService
{
private projectContext _db;
internal UserService(projectContext db)
{
_db = db;
}
public User GetUserByName(string name)
{
return _db.Users.SingleOrDefault(x => x.UserName == name);
}
I've written a custom HttpModule that implements the Application_PreRequestHandlerExecute and Application_PostRequestHandlerExecute events in order to log all request times. I'm trying to instrument an MVC web application with this HttpModule to log the performance of all requests into it. The MVC web application uses Http Session with the timeout set to the default 20 minutes. We have a suite of JMeter load tests that, prior to my HttpModule, were running fine. However, when I instrument the application, after 20 minutes of JMeter running its tests, it starts throwing errors all over the place that indicate that all the session variables have been dropped.
Why is this happening? I'm not accessing session state at all in my HttpModule, so I'm at a loss for what might be causing this. Here's the full code for the custom HttpModule. It seems that the mere presence of this HttpModule is causing session timeout problems for any apps configured to use it.
public class LoggerModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
if (context == null)
{
return;
}
// Using PreRequestHandlerExecute/PostRequestHandlerExecute instead of BeginRequest/EndRequest
// from: http://stackoverflow.com/questions/12981298/how-to-analyze-the-performance-of-requests-in-asp-net-mvc-application
// NOTE: The Web.config for each application should be configured with preCondition="managedHandler". See
// http://stackoverflow.com/questions/18989449/how-does-precondition-managedhandler-work-for-modules.
// TL;DR is that this HttpModule will only run for managed resources like MVC endpoints, .aspx requests, etc. and won't run for static files.
context.PreRequestHandlerExecute += this.Application_PreRequestHandlerExecute;
context.PostRequestHandlerExecute += this.Application_PostRequestHandlerExecute;
}
private void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
// Store the current time in order to determine the time taken to process each request.
(sender as HttpApplication).Context.Items["PerformanceLoggerStart"] = DateTime.Now;
}
private void Application_PostRequestHandlerExecute(object sender, EventArgs e)
{
HttpContext context = (sender as HttpApplication).Context;
if (context == null)
{
return;
}
// Calculate the time taken to process the request
DateTime start = (DateTime)context.Items["PerformanceLoggerStart"];
double millisecondsTaken = (DateTime.Now - start).TotalMilliseconds;
// Asynchronously log performance. Note that this also insulates the profiled application from any exceptions that may be thrown by the task being done.
Task.Run(() => Logger.LogPerformance(new LogEntry
{
// Get the full URL as it appears in the browser MINUS any MVC route-specific parameter like a GUID
Endpoint = ApplicationInformation.TrimIdFromMVCRoute(context.Request.Url),
// GET, POST, etc.
Action = context.Request.HttpMethod,
// Name of the machine executing this code
MachineName = Environment.MachineName,
// Get the application name from the URL - depends on whether running locally or remotely
ApplicationName = ApplicationInformation.GetApplicationName(context.Request.Url, context.Request.ApplicationPath),
// Time taken to process the request
MillisecondsTaken = millisecondsTaken
}));
}
}
I'm working with .NET 3.5 with a simple handler for http requests. Right now, on each http request my handler opens a tcp connection with 3 remote servers in order to receive some information from them. Then closes the sockets and writes the server status back to Context.Response.
However, I would prefer to have a separate object that every 5 minutes connects to the remote servers via tcp, gets the information and keeps it. So the HttpRequest, on each request would be much faster just asking this object for the information.
So my questions here are, how to keep a shared global object in memory all the time that can also "wake" an do those tcp connections even when no http requests are coming and have the object accesible to the http request handler.
A service may be overkill for this.
You can create a global object in your application start and have it create a background thread that does the query every 5 minutes. Take the response (or what you process from the response) and put it into a separate class, creating a new instance of that class with each response, and use System.Threading.Interlocked.Exchange to replace a static instance each time the response is retrieved. When you want to look the the response, simply copy a reference the static instance to a stack reference and you will have the most recent data.
Keep in mind, however, that ASP.NET will kill your application whenever there are no requests for a certain amount of time (idle time), so your application will stop and restart, causing your global object to get destroyed and recreated.
You may read elsewhere that you can't or shouldn't do background stuff in ASP.NET, but that's not true--you just have to understand the implications. I have similar code to the following example working on an ASP.NET site that handles over 1000 req/sec peak (across multiple servers).
For example, in global.asax.cs:
public class BackgroundResult
{
public string Response; // for simplicity, just use a public field for this example--for a real implementation, public fields are probably bad
}
class BackgroundQuery
{
private BackgroundResult _result; // interlocked
private readonly Thread _thread;
public BackgroundQuery()
{
_thread = new Thread(new ThreadStart(BackgroundThread));
_thread.IsBackground = true; // allow the application to shut down without errors even while this thread is still running
_thread.Name = "Background Query Thread";
_thread.Start();
// maybe you want to get the first result here immediately?? Otherwise, the first result may not be available for a bit
}
/// <summary>
/// Gets the latest result. Note that the result could change at any time, so do expect to reference this directly and get the same object back every time--for example, if you write code like: if (LatestResult.IsFoo) { LatestResult.Bar }, the object returned to check IsFoo could be different from the one used to get the Bar property.
/// </summary>
public BackgroundResult LatestResult { get { return _result; } }
private void BackgroundThread()
{
try
{
while (true)
{
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://example.com/samplepath?query=query");
request.Method = "GET";
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8))
{
// get what I need here (just the entire contents as a string for this example)
string result = reader.ReadToEnd();
// put it into the results
BackgroundResult backgroundResult = new BackgroundResult { Response = result };
System.Threading.Interlocked.Exchange(ref _result, backgroundResult);
}
}
}
catch (Exception ex)
{
// the request failed--cath here and notify us somehow, but keep looping
System.Diagnostics.Trace.WriteLine("Exception doing background web request:" + ex.ToString());
}
// wait for five minutes before we query again. Note that this is five minutes between the END of one request and the start of another--if you want 5 minutes between the START of each request, this will need to change a little.
System.Threading.Thread.Sleep(5 * 60 * 1000);
}
}
catch (Exception ex)
{
// we need to get notified of this error here somehow by logging it or something...
System.Diagnostics.Trace.WriteLine("Error in BackgroundQuery.BackgroundThread:" + ex.ToString());
}
}
}
private static BackgroundQuery _BackgroundQuerier; // set only during application startup
protected void Application_Start(object sender, EventArgs e)
{
// other initialization here...
_BackgroundQuerier = new BackgroundQuery();
// get the value here (it may or may not be set quite yet at this point)
BackgroundResult result = _BackgroundQuerier.LatestResult;
// other initialization here...
}
so I have succeeded in connecting my Windows Phone 8 Application to the Live API, I also succeeded in reading data from my hotmail account.
I have access to the needed client ID and the live access token.
But when I quit and restart my application, I lose all references to the session and the client objects and I have to start the process anew.
I don't want to annoy the user with the web mask in which he has to agree again that he provides me with the needed permissions every time he is starting the application. But I also haven't found a way to get a reference to a session object without this step.
The login mask is only shown the first time after installing the application, after that, only the screen mentioned above is shown.
But it is still quite annoying for the user to accept this every time.
I already tried serializing the session object, which is not possible, because the class does not have a standard constructor.
Maybe it is possible to create a new session by using the live access token, but I haven't found a way to do so.
Any ideas? What am I doing wrong, I know that there is a way to login again without prompting the user.
I'm thankful for every idea.
Some code I use:
/// <summary>
/// This method is responsible for authenticating an user asyncronesly to Windows Live.
/// </summary>
public void InitAuth()
{
this.authClient.LoginCompleted +=
new EventHandler<LoginCompletedEventArgs>(this.AuthClientLoginCompleted);
this.authClient.LoginAsync(someScopes);
}
/// <summary>
/// This method is called when the Login process has been completed (successfully or with error).
/// </summary>
private void AuthClientLoginCompleted(object sender, LoginCompletedEventArgs e)
{
if (e.Status == LiveConnectSessionStatus.Connected)
{
LiveConnector.ConnectSession = e.Session; // where do I save this session for reuse?
this.connectClient = new LiveConnectClient(LiveConnector.ConnectSession);
// can I use this access token to create a new session later?
LiveConnector.LiveAccessToken = LiveConnector.ConnectSession.AccessToken;
Debug.WriteLine("Logged in.");
}
else if (e.Error != null)
{
Debug.WriteLine("Error signing in: " + e.Error.ToString());
}
}
I have tried to use the LiveAuthClient.InitializeAsync - method to login in background after restarting the application, but the session object stays empty:
// this is called after application is restarted
private void ReLogin()
{
LiveAuthClient client = new LiveAuthClient(LiveConnector.ClientID);
client.InitializeCompleted += OnInitializeCompleted;
client.InitializeAsync(someScopes);
}
private void OnInitializeCompleted(object sender, LoginCompletedEventArgs e)
{
Debug.WriteLine("***************** Inititalisation completed **********");
Debug.WriteLine(e.Status); // is undefined
Debug.WriteLine(e.Session); // is empty
}
Does anyone have an idea how I could get access to a new session after restarting the application?
After two full days searching for the mistake I was making, I finally found out what I was doing wrong: I have to use the wl.offline_access scope to make this work!
Let me quote another user here:
"If your app uses wl.offline_access scope than the live:SignInButton control saves it for you and loads it automatically. Just use the SessionChanged event to capture the session object. This way the user will need to sign in only once."
(see WP7 how to store LiveConnectSession during TombStoning?)
Now everything is fun again. Can't believe that this was the problem. Tested & working. Nice!
Been struggling to get this working on a Windows Live + Azure Mobile Service app myself so thought I would post a complete working code sample here now that I've got it working.
The key parts are the wl.offline_access scope and the call to InitializeAsync.
Note: this sample also connects with Windows Azure Mobile Services. Just remove the stuff related to MobileService if you're not using that.
private static LiveConnectSession _session;
private static readonly string[] scopes = new[] {"wl.signin", "wl.offline_access", "wl.basic"};
private async System.Threading.Tasks.Task Authenticate()
{
try
{
var liveIdClient = new LiveAuthClient("YOUR_CLIENT_ID");
var initResult = await liveIdClient.InitializeAsync(scopes);
_session = initResult.Session;
if (null != _session)
{
await MobileService.LoginWithMicrosoftAccountAsync(_session.AuthenticationToken);
}
if (null == _session)
{
LiveLoginResult result = await liveIdClient.LoginAsync(scopes);
if (result.Status == LiveConnectSessionStatus.Connected)
{
_session = result.Session;
await MobileService.LoginWithMicrosoftAccountAsync(result.Session.AuthenticationToken);
}
else
{
_session = null;
MessageBox.Show("Unable to authenticate with Windows Live.", "Login failed :(", MessageBoxButton.OK);
}
}
}
finally
{
}
}
I'm a IT student, second year. We just learned to program with 3 layers, one for getting data with a class, one for manipulating stuff with requests (all of the methods go in here) and one for the working of the program itself. Seeing as the first two go into classes instead of a form I dont know how to show errors.
Example:
We need to make a login system with a webbrowser and some other stuff behind it. So I make the login in a class, but how to check back for errors? I don't think it's normal or even possible to do MessageBox.Show(error); from a class, I can only return stuff, but I want the username/id to be returned if possible.
So in short, what is the best/most accepted way to report errors that are caused by data, so from a class?
Your framework level API's (eg. your layers) should use Exceptions for real errors, and return values to report non-critical errors.
public class Login
{
public bool AccountExists(string name) {
bool exists;
// do checking
return exists;
}
public LoginResult Login(string name, string password) {
// Try login
// If successful
return LoginResult.Success;
// What if the user does not exist?
return LoginResult.AccountNotFound;
// What about an error?
return LoginResult.Error;
}
}
public enum LoginResult
{
None,
AccountNotFound,
Error,
Success
}
In the example above, you can report the status of operations through return values. For LoginResult this could even be a value type (struct) that contains more information about the result (eg. a string message or something). Because these types of operations on non-critical, there is no necessity for exceptions here. Exceptions are costly and not always necessary to report errors.
Now let's talk about a different type of error. Logical developer errors. These should be handled by throwing exceptions. Take this example (assume we have some type Account that has a Role property).
public class Foo
{
public bool IsAdmin(Account account) {
if (account == null) {
throw new System.ArgumentNullException("You cannot pass a null account!");
}
return account.Role == "Admin";
}
}
We know as a developer that the account should not be null, so we should check for it and throw an exception if it is. If this exception is ever thrown, its a bug in the calling code and should be fixed not to pass in a null value.
Now that I've given two rough scenarios, how does this apply to your question? These are API's. Whatever your UI layer is, whether it be a WinForm, WPF Window, WebForm, or some other UI, the UI only has to use the API. The API is responsible for reporting information that can be usable by the UI, and the UI is responsible for displaying info in whatever way is best suited for that UI.
The framework API layers should never be responsible for reporting an error to the user with a UI. They should only be responsible for reporting errors to the developer who can take the result, and wire it up to the UI layer in some fashion. You would never display a message box or write to a console from a framework API for example. You would return some result that the UI can use to display its own message.
I highly recommend that you read Framework Design Guidelines. It covers a lot of this material and is an extremely great read.
You should have a class which validates the data object and returns error information. Then your front-end code can ask this class to validate the data and show any error messages that get returned.
var username = GetUserName();
var password = GetPassword();
var validationResult = new Validator().ValidateLogin(username, password);
if(validationResult.ErrorMessage != null) {
MessageBox.Show(validationResult.ErrorMessage);
} else {
// Do what you would have done.
}
If any errors occur that are outside of the expected logic flow, they should throw an exception.
Well you can use Exceptions. You Just throw the exception, it is up to the caller on what to do with the exception.
class Login
{
public Login()
{
}
public bool CheckLogin(string userName, string password)
{
// Do your validation here.
If every thing goes fine
return True.
else
throw Exception("custom message.");
}
}
class Input //class which takes input.
{
Login login = new Login();
public void TakeInput(string username, string password)
{
try
{
login.CheckLogin(username, password);
}
catch(Exception ex)
{
MessageBox.show(ex.message);
}
}
}