分享
 
 
 

Using ASP.NET Session State in a Web Service

王朝asp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

Quick Overview of ASP.NET Sessions

ASP.NET session state is maintained by using one of two underlying

mechanisms. The first is by using HTTP cookies. The idea behind HTTP

cookies is that when the client sends a request, the server sends back

a response with an HTTP Set-Cookie header that has a name/value pair in

it. For all subsequent requests to the same server, the client sends

the name/value pair in an HTTP Cookie header. The server then can use

the value to associate the subsequent requests with the initial

request. ASP.NET uses a cookie that holds a session ID to maintain

session state. Then that ID is used to find the corresponding instance

of the HttpSessionState class for that particular user. The HttpSessionState class provides just a generic collection in which you can store any data that you want.

The other mechanism that ASP.NET uses for maintaining session state

works without cookies. Some browsers do not support cookies or are not

configured to keep and send cookies. ASP.NET provides a mechanism for

getting around this problem by redirecting a request to a URL that has

the ASP.NET session ID embedded in it. When a request is received, the

embedded session ID is simply stripped out of the URL and is used to

find the appropriate instance of the session object. This works great

for browsers that are doing HTTP GET requests, but creates issues when

writing Microsoft® .NET code that consumes an XML Web service.

It should be noted that sometimes it makes sense to store state

information in cookies themselves instead of in the ASP.NET session

object. By avoiding the session object, you use fewer resources on the

server, and you do not have to worry about issues like locating a

specific instance of the session object across a Web farm, instances of

the session object being cleaned up because of a long delays between

requests, or session instances lingering around for no reason until

their timeout period expires. However, if you have data that includes

implementation information that you do not want to share with the

consumers of your service, or is private data that you do not want to

send across an unencrypted channel, or if the data would be impractical

to serialize into an HTTP header, then it may make sense to take

advantage of the HttpSessionState class in ASP.NET. The HttpSessionState class returns an index key that is used to map a particular user to an instance of the HttpSessionState class that holds information stored for that user. Both the ASP.NET HttpSessionState class and HTTP cookies are available to users writing ASP.NET Web services.

Why Use an HTTP Mechanism for Maintaining State in an XML Web Service?

There are many ways to maintain state between SOAP requests.

Certainly one feasible option would be to include something like the

ASP session ID in the SOAP header of your SOAP message. The problem is

that you have to: 1) still write the server side code yourself, and 2)

make sure your clients treat your session ID header like an HTTP cookie

and send it back to you with each request. There are certainly cases

where using the SOAP header approach makes a lot of sense, but there

are situations where using the HTTP approach can make sense as well.

ASP.NET session state is already done for you. The HttpSessionState

class is available for easily storing your session objects. Most HTTP

clients already understand that they must return the cookies that are

set by the server and HttpSessionState happens to support the

underlying transport most frequently used for SOAP communications—HTTP.

Thus it makes sense that using ASP.NET session support could be a smart

decision to meet many state management requirements.

Enabling Session Support on the Server

By default, ASP.NET session support for each Web method is turned

off. You must explicitly enable session support for each Web method

that wants to use session state. This is done by adding the EnableSession property to the WebMethod attribute of your function. The code for a Web method with the EnableSession property set to true, and which accesses the HttpSessionState object, is shown below.

<WebMethod(EnableSession:=True)> _

Public Function IncrementSessionCounterX() As Integer

Dim counter As Integer

If Context.Session("Counter") Is Nothing Then

counter = 1

Else

counter = Context.Session("Counter") + 1

End If

Context.Session("Counter") = counter

Return counter

End Function

As you might expect, if you enable session support for one Web

method, that does not imply that it is enabled for another Web method.

In fact, the Context.Session property will be null if EnableSession is not explicitly set to True for a particular Web method.

Be aware that it is possible to disable sessions by way of a web.config setting, so that even if you use the EnableSession property in your WebMethod attribute, Context.Session will always be null. The /configuration/system.web/sessionState element

has a mode attribute that is used to configure how session state is

maintained for your ASP.NET application. By default the mode is set to

"InProc," which means that the HttpSessionState objects will

simply be held in the ASP.NET process' memory. If the mode is set to

"Off," then there will be no session state support in the ASP.NET

application.

From the HTTP server standpoint, the scope of an ASP.NET session is

that it lives within a given ASP.NET application. This means that the

same instance of the HttpSessionState class will be used for

all session-enabled ASP.NET requests within a single virtual directory

for a particular user. A request to a different virtual directory with

the same session ID cookie will result in ASP.NET being unable to find

the corresponding session object—because the session ID was set for a

different ASP.NET application. ASP.NET does not differentiate between

ASPX and ASMX requests as far as sessions are concerned, so you could

theoretically share session state between a Web method call and a

normal ASPX file. However, there are client-side issues that we will

look at in a little bit that might make this tricky.

When setting an HTTP cookie, you can associate an optional

expiration time with it. The expiration time indicates how long the

client should continue sending the cookie back to the server. If a

cookie is set without the optional expiration, it will only be returned

for the life of the process making the requests. For instance,

Microsoft® Internet Explorer will return the cookie until you close

that particular instance of your browser. The session ID cookies used

by ASP.NET do not have expiration times. Therefore, if multiple

processes on a client machine are making HTTP requests to your server,

then they will not share the same HttpSessionState object. This is true even if the two processes are running at the same time.

If you are making simultaneous Web service calls from the same

process, the requests will be serialized at the server so that only one

will execute at any one time. Unlike .ASPX pages that have support for

read-only access to the HttpSessionState object, which allows

for simultaneous processing of multiple requests, there is no such

capability with ASP.NET Web services. All Web method calls with

sessions enabled have read/write access and will be serialized within

each session.

Client-Side Issues

Successfully using the HttpSessionState capabilities in your

Web service does rely upon some assumptions about the consumers of your

Web service. First and foremost, if you are using the default HTTP

cookie mode of maintaining session state, then your clients must

support HTTP cookies. If you are using the cookieless mechanism for

supporting sessions, then your clients must be able and willing to

redirect their requests to the modified URLs with the session IDs in

them. As it turns out, this is not a trivial assumption, even with a

.NET client application.

Everything Works from the Browser

If you develop an ASP.NET Web service in Microsoft® Visual Studio®

.NET, the default debugging behavior is to launch Internet Explorer and

browse to your .ASMX file. This usually will result in a friendly HTML

interface for invoking your Web methods. This turns out to be a nice

way to debug your Web service code, and if you have set the EnableSession

property to True for your Web method, it tends to work out beautifully.

Even if you turn on cookieless session support, the browser client will

work perfectly, and your session will work in the manner that you

expect it to.

However, most Web service requests do not come from a browser. What

happens when you create a client application that uses the "Add Web

Reference" feature of the .NET Framework? Let's take a look at the

results.

Problems Using Add Web Reference

I created a simple XML Web service using the code snippet that we saw earlier. If you recall, the Web method is called IncrementSessionCounter and simply stores an integer in the HttpSessionState

object, increments it with each call, and returns the current value.

From the browser client, we see that the number increases by one with

each invocation as we expect.

Next, I created a simple Microsoft® Windows Form application and

added a Web reference for my Web service. The code for invoking my Web

service looks like this:

' Does NOT work with ASP.NET Sessions

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click

Dim proxy As New localhost.Service1()

Dim ret As Integer

ret = proxy.IncrementSessionCounter()

Label1.Text = "Result: " & CStr(ret)

End Sub

When I invoke the Web service the first time, everything works as

expected. The Web method returns the initial value for my session

variable, which is 1. Now if I click on Button1 to invoke my Web method

again, I expect to see a returned value of 2. However, no matter how

many times I click on Button1, I always see a value of 1 returned.

You might suspect the cause of this is that I'm creating a new

instance of the proxy class for my Web service, so each time I click on

the button, I am losing my cookies (so to speak). Unfortunately, even

if you move the proxy initialization code into the constructor for your

Form class and use the same instance of the proxy for each Web

method call, you still will not see the session variable return with a

value greater than 1.

The problem is with the cookies. The Web service code does not see a

valid session ID with the request, so it creates a brand new HttpSessionState

object for each call, and returns the initial value of 1. The reason

for this is that the client proxy class, which inherits from the System.Web.Services.Protocols.SoapHttpClientProtocol class does not have an instance of the System.Net.CookieContainer class

associated with it. Basically, there is no place to store cookies that

are returned. To fix this problem, I changed my code as follows with

the new code highlighted:

' Works with cookied ASP.NET sessions but NOT with

' cookieless sessions.

Private Cookies As System.Net.CookieContainer

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click

Dim proxy As New localhost.Service1()

Dim ret As Integer

' Set the Cookie Container on the proxy

If Cookies Is Nothing Then

Cookies = New System.Net.CookieContainer()

End If

proxy.CookieContainer = Cookies

ret = proxy.IncrementSessionCounter()

Label1.Text = "Result: " & CStr(ret)

End Sub

And now the code works as expected! With each click of Button1, I

see the returned value increase by 1. Note that the Cookies variable is

not declared inside my function. It is a private member of my form

class. I need to use the same instance of the CookieContainer

class with each request if I expect the same session ID cookie to be

returned to the server. This explains why a default cookie container is

not automatically associated with an instance of the SoapHttpClientProtocol

class. There is a good chance that you would want to use a separately

managed cookie container that could be shared among multiple instances

of the SoapHttpClientProtocol class, instead of automatically creating a new cookie container for each instance.

Cookieless Sessions

From the standpoint of the Web service developer, you might think

that quite a few people trying to consume your service will forget to

add a cookie container to their client proxies. With a clever twinkle

in your eye, you also might think that cookieless sessions may be the

perfect solution to this problem. If you set the cookieless attribute of the sessionState

element to "true" in your web.config, you will notice that sessions

still work perfectly when invoking your Web methods using the browser

interface. Unfortunately, there are still issues if you use the "Add

Web Reference" capabilities within Visual Studio .NET.

To investigate cookieless sessions, I decided to take the client

code I used above and simply see if it would work for a Web service

that was configured for cookieless sessions. I did not bother to delete

the cookie container code, because I wanted to have code that would

work with traditional cookied sessions as well as cookieless sessions.

Being a bit of an optimist, I simply ran the code as is.

Disappointingly, but not completely unexpectedly, I witnessed the

following exception:

An unhandled exception of type 'System.Net.WebException' occurred in system.web.services.dll

Additional information: The request failed with the error message:

--

<html><head><title>Object moved</title></head><body>

<h2>Object moved to <a href='/HttpSessionState/(l2z3psnhh2cf1oahmai44p21)/service1.asmx'>here</a>.</h2>

</body></html>

What happened is that the HTTP request received a response that was

not a "200 OK" HTTP response. For those of you familiar with HTTP, you

probably can correlate the HTML listed in the response shown as

indicating that this was a "302 Found" HTTP response. This means that

the request was redirected to the URL indicated in the hyperlink. The

HTML returned is actually just a nice thing that a browser can show if

for some reason it does not support redirects, or until the redirected

request completes. If you look at the hyperlink, you will notice that

the href includes an interesting substring of

"(l2z3psnhh2cf1oahmai44p21)". If you have been paying attention, you

have probably correctly deduced that this is the ASP.NET session ID,

and it has been embedded in the URL that we have been redirected to.

What we need is for our client proxy class to resend the request to

this new URL.

Having done more than my share of programming with the old Win32

WinInet API, I went looking for a property on our proxy class that

would allow me to turn on auto redirects. In layman's terms, this

simply means that if we received an HTTP response of "302 Found," we

would simply resend the request to the URL indicated by the HTTP

Location header in the response. I was feeling pretty smart when the

Microsoft® IntelliSense® in Visual Studio .NET showed me the AllowAutoRedirect property on my proxy class. I quickly added the following line to my code:

proxy.AllowAutoRedirect = True

I gave my program another try, thinking this was still slightly easier than creating a CookieContainer class, and assigning it to my proxy. I got the following exception (truncated for brevity):

An unhandled exception of type 'System.InvalidOperationException' occurred

in system.web.services.dll

Additional information: Client found response content type of 'text/html; charset=utf-8',

but expected 'text/xml'.

The request failed with the error message: …

If you looked at the contents of the error message, you would find

that you were looking at the HTML page that you see when you browse to

your .ASMX file. The question you might have is: Why it is returning

HTML when I am posting XML (in the form of a SOAP envelope) to the Web

service? As it turns out, you did not send an HTTP POST request with a

SOAP envelope, you simply sent an HTTP GET request with no body, and

your Web service appropriately assumed you were a browser and returned

its normal HTML response. How could this happen?

If you read the HTTP specification,

you will find that it is appropriate for an HTTP client to send an HTTP

GET request to the indicated URL in reaction to an HTTP "302 Found"

response, even if the initial request was an HTTP POST. This works

great with browsers, because just about all of their requests are HTTP

GET requests in the first place. It does not work well when you see

this result when you are posting data to a URL.

The justification for this is that potentially sensitive data may be

contained in the posted data, so you need to confirm with the user if

they really want to send the data to the new resource. If you are going

to the new location based off an auto-redirect setting, you are

obviously failing to confirm with the user whether it is okay to post

their data to a new location. Therefore the data is not sent, and a

simple HTTP GET request is sent instead.

I made the following modifications to set the URI on the proxy, catch the "302 Found" WebException,

prompt the user for permission to redirect their request, and call my

function again with the new location (changes from the previous code

are highlighted):

' Works with both cookied and cookieless ASP.NET sessions.

Private Cookies As System.Net.CookieContainer

Private webServiceUrl as Uri

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click

Dim proxy As New localhost.Service1()

Dim ret As Integer

' Set the Cookie Container on the proxy

If Cookies Is Nothing Then

Cookies = New System.Net.CookieContainer()

End If

proxy.CookieContainer = Cookies

' Set the Url on the proxy

If webServiceUrl Is Nothing Then

webServiceUrl = New Uri(proxy.Url)

Else

proxy.Url = webServiceUrl.AbsoluteUri

End If

Try

ret = proxy.IncrementSessionCounter()

Catch we As WebException

' We need an HttpWebResponse if we expect to

' check the HTTP status code.

If TypeOf we.Response Is HttpWebResponse Then

Dim HttpResponse As HttpWebResponse

HttpResponse = we.Response

If HttpResponse.StatusCode = HttpStatusCode.Found Then

' This is a "302 Found" response. Prompt the user

' to see if it is okay to redirect.

If MsgBox(String.Format(redirectPrompt, _

HttpResponse.Headers("Location")), _

MsgBoxStyle.YesNo) = _

MsgBoxResult.Yes Then

' It is okay. Set the new location and

' try again.

webServiceUrl = New Uri(webServiceUrl, _

HttpResponse.Headers("Location"))

Button1_Click(sender, e)

Return

End If

End If

End If

Throw we

End Try

Label1.Text = "Result: " & CStr(ret)

End Sub

And now the ASP.NET session code works as expected. For the purposes

of your own application, you can determine whether you need to prompt a

user for redirecting their HTTP POST request or not. For instance, if

you were calling this code from a service, you would not want to create

a dialog that could not be seen.

This may seem to be a lot of work for getting ASP.NET sessions to

work properly, but be aware that the code shown is useful for other

things as well. For instance, any Web service on any platform that uses

HTTP cookies would require the cookie container code. Similarly, there

may be a host of other reasons why you might receive a "302 Found"

response in reply to your request to a Web service. In a robust

application, there will probably be a number of special scenarios that

you will want to handle when invoking a Web service. Handling cookies

and redirects are two such scenarios you may want to include in your

Web service invocation code on a regular basis.

Conclusion

ASP.NET sessions can be very useful for maintaining state between

Web method calls in your Web service. You do need to be aware that

there may be issues that must be handled by client applications that

you may not see when testing your Web service with the convenient

browser interface. Fortunately, these issues are not particularly hard

to handle.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有