I have a webshop running which contains parts of cars. The prices next to the parts are loaded from a webservice running else where. This webservice only contains one webmethod: GetArticleInformation.
In this webservice there is a link to another webservice WebshopServiceClient running elsewhere which contains the info about the cars and holds the prices.
Now when a user select a part of the vehicle he wants to buy the first webservice is called and the method GetArticleInformation is executed. In this method I want to create a session which hold the logon of the second webservice ( the database ). In this way I want to prevent that for every call a new logon is required.
[WebMethod(EnableSession = true)]
public GetBackItems GetArticleInformation(User user, Items items)
{
//Create session if needed
client = (WebshopServiceClient)Session["SphinxLogon"];
if (client == null)
{
client = new WebshopServiceClient();
bool done = client.Logon();
if (done)
{
Session["SphinxLogon"] = client;
}
}
//Get information and send it back
...
}
Now when the user in the webshop selects a part the session is created but the next time the user selects a part the session is null again.
What am I doing wrong or what am I missing?
I would consider 'talking' to the various web-services via an internal 'proxy'-procedure -- fired up on app-start for example -- which would handle all traffic etc with the services. That way the individual client sessions do not have to logon or maintain a session with the services but can still be managed via the proxy. Individual clients would get a 'ticket' from the proxy which then could be part of their session and could be used to manage it.
Related
I use ASP.NET MVC. I have a problem. I set my variables to the session and I request a web service that doesn't belong to me. Then the web service makes an HttpPost request to my server.
It doesn't send a cookie to my server so I lost my session.
I think I can save my sessionid to the DB and I can get back my session with this ID. But I can't get any solution.
What's your suggestion?
public ActionResult SomeAction(){
mySettingService.saveSessionIdToDb(someToken, Session.SessionID);
var myPaymentFormObj = FormInit.Create(request, options);
myPaymentFormObj.DoRequest(); //it's callback to my another action with a token
}
[HttpPost]
public ActionView MyCallBack(string someToken){
//here is our app generates new session id and i lost my session because other server doesn't send me session id.
//i need to read session id from db and i can get my session maybe.
var mySessionId = mySettingService.getSessionIdFromDb(someToken);
//how can i start session like this?
Session.SessionID = mySessionId;
}
It seems like the problem you described is about maintaining the distributed transaction.
To describe it better your app is a service A and the webServer is service B.
You can perform an action which saves some changes to the database A including the session stuff then you send a call to service B which also can saves some changes to its DB or perform a bunch of other actions but in this case you don't care how it works, you only care about what kind of responses you get back with a callback. There should be an option to be able to send some kind of unique thing like userEmail or a transactionId which you can get back in a callback method to be able to recognize the transaction.
What I would suggest you do is something like
[HttpPost]
public ActionResult SendBlah(BlahData data){
var transactionId = Guid.NetGuid();
_sessionService.Create(transactionId, dataYouWantToStore)
_webServiceB.SendBlah(transactionId, data, token);
//optionally return info about call status / do other stuff
}
//or this can be of type HttpGet
[HttpPost]
public ActionView MyCallBack(string someToken, string tranactionId){
var sessionData = _sessionService.Get(tranactionId)
//do other stuff
}
If it's needed and you are using e.g. JWT you can store the transactionId/emailAddress/etc. there instead and read it.
Btw. it's always safer to store the session in the database instead of using some cache objects or relaying on cookies or javascript objects etc.
Also, it's better to be careful with the amount of data you want to store in a Session table in your db. I'd personally focus on storing the Ids and stuff like Status of given item etc.
I have a Web API providing a backend to an Angular.JS web application. The backend API needs to track the state of user activities. (Example: it needs to note which content ID a user last retrieved from the API)
Most access to the API is authenticated via username/password. For these instances, it works fine for me to store the user state in our database.
However, we do need to allow "guest" access to the service. For guests, the state does need to be tracked but should not be persisted long-term (e.g. session-level tracking). I'd really like to not have to generate "pseudo users" in our user table just to store the state for guest users, which does not need to be maintained for a significant period of time.
My plan is to generate a random value and store it in the client as a cookie. (for guests only - we use bearer authentication for authenticated users.) I would then store whatever state is necessary in an in-memory object, such as a Dictionary, using the random value as a key. I could then expire items off the dictionary periodically. It is perfectly acceptable for this data to be lost if the Web API is ever relaunched, and it would even be acceptable for the dictionary to be reset say, every day at a certain time.
What I don't know how to do in WebAPI is create the dictionary object, so that it will persist across Web API calls. I basically need a singleton dictionary object that will maintain its contents for as long as the server is running the Web API (barring a scheduled clearing or programmatic flushing)
I had the idea of dumping the Dictionary off to disk every time an API call is made, and then reading it back in when it's needed, but this does not allow for multiple simultaneous in-flight requests. The only method I can think of right now is to add another database table (guest_state or something) and replicate the users table, and then setup some sort of manual method to regularly clean out the data in the guest table.
Summary: what I need is
a way to store some data persistently in a Web API backend without having to go off to a database
preferably store this data in a Dictionary object so I can use randomly-generated session IDs as the key, and an object to store the state
the data is OK to be cleared after a set period of time or on a regular basis (not too frequently, maybe a minimum of a 6 hour persistence)
I figured out a solution using the Singleton pattern:
public static class Services
{
private static Dictionary<string, string> cache;
private static object cacheLock = new object();
public static Dictionary<string,string> AppCache
{
get
{
lock (cacheLock)
{
if (cache == null)
{
cache = new Dictionary<string, string>();
}
return cache;
}
}
}
}
public class testController()
{
[HttpGet]
public HttpResponseMessage persist()
{
HttpResponseMessage hrm = Request.CreateResponse();
hrm.StatusCode = HttpStatusCode.OK;
Services.AppCache.Add(Guid.NewGuid().ToString(), DateTime.Now.ToString());
string resp = "";
foreach (string s in Services.AppCache.Keys)
{
resp += String.Format("{0}\t{1}\n", s, Services.AppCache[s]);
}
resp += String.Format("{0} records.", Services.AppCache.Keys.Count);
hrm.Content = new StringContent(resp, System.Text.Encoding.ASCII, "text/plain");
return hrm;
}
}
It seems the Services.AppCache object successfully holds onto data until either the idle timeout expires or the application pool recycles. Luckily I can control all of that in IIS, so I moved my app to its own AppPool and setup the idle timeout and recycling as appropriate, based on when I'm ok with the data being flushed.
Sadly, if you don't have control over IIS (or can't ask the admin to set the settings for you), this may not work if the default expirations are too soon for you... At that point using something like a LocalDB file or even a flat JSON file might be more useful.
say Rest service got one request which is giving a call to another service (email service),
but when Rest service got another request which is giving a call to email service I want it to skip that step.
also rest service should be intelligent enough to see if email process is being running or stopped if stopped any next request to rest service is allowed to call email service.
Rest Method : -
void SaveDataAndCallEmailService(Data data)
{
cntx.Add(data);
cntx.SaveChanges();
SendEmail();
}
now my query is how to make "SendMail()" method to be called only once only if Email Process is not running.
for e.g. : -
void SaveDataAndCallEmailService(Data data)
{
cntx.Add(data);
cntx.SaveChanges();
if(!EmailProcessIsRunning)
SendEmail();
EmailProcessIsRunning= true;
}
Edit: -
Now, here "EmailProcessIsRunning" is always false, even though I marked it as true.
When server receive second request I found "EmailProcessIsRunning" is false again.
How to persist it's value?
In your code sample EmailProcessIsRunning is not defined anywhere. Makes if more difficult to see if there's a scope issue.
Assuming there's no scope issues, if you want to persist the value saved to EmailProcessIsRunning between requests of on a stateless webapi server you'll need to add a DependecyResolver to your server. Read this for more information http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver
edit: (a simpler answer)
You can add a static property to your class, and set it to true when the process is started.
e.g.
public bool static EmailProcessIsRunning { get; set; }
When the user makes selection and clicks a button, I call to:
public ActionResult Storage(String data)
{
Session["Stuff"] = data;
return null;
}
Then, I redirect them to another page where the data is accessed by
#Session["Stuff"]
This far, I'm happy. What I do next is that upon a click on a button on the new page, I perform a call to:
public ActionResult Pdfy()
{
Client client = new Client();
byte[] pdf = client.GetPdf("http://localhost:1234/Controller/SecondPage");
client.Close();
return File(pdf, "application/pdf", "File.pdf");
}
Please note that the PDFization itself works perfectly well. The problem is that when I access the second page a second time (it's beeing seen by the user and looks great both in original and on reload), it turns out that Session["Stuff"] suddenly is null!
Have I started a new session by the recall?
How do I persistently retain data stored in Session["Stuff"] before?
If you're simply storing string data (as would be indicated by your method signature) in an MVC application, don't.
It's far easier to pass the data as a query parameter to each method that needs it. It's far easier to manage and doesn't rely on Session sticky-ness.
To generate the appropriate links, you can pass data to your views and use Html.ActionLink to generate your links with the appropriate parameter data.
Here's several reasons why the session variable could return null:
null is passed into Storage
Some other code sets Session["Stuff"] to null
The session times out
Something calls Session.Clear() (or Session.Abandon())
The underlying AppPool is restarted on the server
Your web server is farmed and session state is not distributed properly
The first two can be discovered by debugging.
When every I attempt to add a new server callback function I cannot seem to get the callback to show up in the $.connection server callback list.
What do I need to do to refresh the javascript that SignalR produces and sends to the client with the latest list of server callbacks.
I would think that I should be able to just add a server callback for the client to call and rebuild my app and fire up a new instance of Google Chrome and the newly added server callback would be in the list of available callbacks on the client.
For example here is exactly what I've done.
1.) A client joins a group and everyone is notified.
public override Task OnConnected()
{
string pid = this.Context.QueryString["pid"],
uid = this.Context.QueryString["uid"],
ispro = this.Context.QueryString["ispro"];
Groups.Add(this.Context.ConnectionId, pid);
return Clients.Group(pid).joined(new cmsg(Context.ConnectionId, UtilCommon.GetUserMini(new Guid(uid), bool.Parse(ispro))));
}
2.) On the client the joined function is called from the server
this.collaborateHub.client.joined = function (cmsg) {
//
that.chatBox.addAttendee(cmsg);
//let the new attendee know about you
if (cmsg.cnnid !== that.chatBox.getMeAttendee().cnnid) {
that.chatBox.getMeAttendee().newbieid = cmsg.cnnid;
debugger
this.server.addMeNewbie(that.chatBox.getMeAttendee());
};
};
3.) Now, if someone joined and it was not the user of the currently opened window, that means the someone how just joined is someone other than myself, so I need to call the server back and notify the newly joined user to add me to their user list. I stead of having to keep up with the currently signed on users to the given group in a database, I am just using every signed on client of the group as a distributed database and let them manage their own info.
So, I need to call the server callback named addMeNewbie; however, this callback function is not available in the list.
public Task addMeNewbie(cmsg cmsg) {
return Clients.Client(cmsg.newbieid).addCurrAttendee(cmsg);
}
Here is a snapshot of the client side in debug mode
4.) And finally here is the client side callback that i need the server to call so that the newly joined group member can update their list of current group members.
this.collaborateHub.client.addCurrAttendee = function (cmsg) {
debugger
that.chatBox.addAttendee(cmsg);
};
I would think that I should be able to add as many server callbacks as I want and they should show up on the next build and restart of a new instance of Google Chrome; however, this is not the case. What do I need to do to get newly added server callbacks to show up in the available server callbacks on the client side?
This doesn't make since to me, but I am thinking maybe this is a cashing issue or something?
The only callback that is available is the one shown in the snapshot provided. At this time I've only got two callbacks, the one that you see in the snapshot and the one that is not visible.
Somewhere along the way I created a signalr-hubs.js file from the generated JavaScript file that can be retrieved by http://localhost:3250/signalr/hubs and was adding new server functions to my hub but not updating the signalr-hugs.js file.
At this point every time I add a new server callback I retrieve the newly generated proxy by placing this url http://localhost:3250/signalr/hubs into the browser, coping the code, and then updating my signalr-hubs.js file.