Cookies not preserved in ASHX handler - asp.net

This is a single page application which uses a cookie to hold a guid of the users server login. Server httpgets are all wrapped into a single ASHX file and are all invoked by jQuery $.ajax(). The only web file in the project is a htm file which hosts all the javascript to make it happen.
When I set a cookie using Response.Cookies I get a different value when I inspect it to Request.Cookies, and when I set a Response.Cookie, the corresponding Request.Cookie on the next server visit still contains some old value, not the value just set.
I have added these two lines to the handler ASHX file:
Implements System.Web.SessionState.IRequiresSessionState
Implements System.Web.SessionState.IReadOnlySessionState
I have also changed the IsReusable property to true:
ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
Neither of the above changes helped.
When I inspect Session.SessionID, its different on every server call, which I suspect is the root of the problem - should it not be the same ?
The webserver is the Visual Studio 2015 debugging web server, not IIS.
Cookies are got using request.Cookies("name").Value and the set function is as follows:
Public Sub SetCookie(ByVal Response As System.Web.HttpResponse, ByVal sCookie As String, ByVal sValue As String, Optional ByVal lTimePeriodMinutes As Integer = 30 * 24 * 60)
sCookie = sCookie.ToLower
Response.Cookies.Remove(sCookie)
Dim obj As New System.Web.HttpCookie(sCookie, sValue)
obj.Expires = DateAdd(Microsoft.VisualBasic.DateInterval.Minute, lTimePeriodMinutes, Now)
Response.Cookies.Add(obj)
End Sub
The above has worked for many years on an old version of the app.
The problem is the same on IE and Firefox

Related

How to store and access per call data in WCF

I'm trying to set up some WCF services that are connected and pass a custom HTTP header from service to service.
That is my client call ServiceX, which calls ServiceY, which writes to the DB.
They are originally called from a Silverlight 5 client in some cases, other cases from an ASP.NET web app.
I implemented IClientMessageInspector and IDispatchMessageInspector to pass the header from service to service, and in the DispatchMessageInspector I wrote the header to an implementation of IExtension(Of OperationContext) (see below).
However, I wanted this data only to exist for the duration of the call, but it seems to be sticking around in the services under certain circumstances as I keep seeing the same header data repeated in different calls.
Ultimately, I want to be able to pass a custom header to a WCF service, persist it only while that call exists, send it to the next service in the header, and wipe out that service instance. Am I wrong in my thinking that using a PerCall WCF service setup and OperationContext is the right way to do that?
Here is my implementation of IExtension(Of OperationContext). The auditTransactionId is the thing I want to pass in the service. As well, the Current() property is where I keep seeing existing data:
Imports System.ServiceModel
Public Class CustomOperationContextExtension
Implements IExtension(Of OperationContext)
Private ReadOnly m_items As IDictionary(Of String, Object)
Private m_auditTransactionId As String
Private Sub New()
m_items = New Dictionary(Of String, Object)()
End Sub
Public ReadOnly Property Items() As IDictionary(Of String, Object)
Get
Return m_items
End Get
End Property
Public Property AuditTransactionId() As String
Get
Return m_auditTransactionId
End Get
Set(value As String)
m_auditTransactionId = value
End Set
End Property
Public Shared ReadOnly Property Current() As CustomOperationContextExtension
Get
If (OperationContext.Current IsNot Nothing) Then
Dim context As CustomOperationContextExtension = OperationContext.Current.Extensions.Find(Of CustomOperationContextExtension)()
If context Is Nothing Then
context = New CustomOperationContextExtension()
OperationContext.Current.Extensions.Add(context)
End If
Return context
End If
Return Nothing
End Get
End Property
Public Sub Attach(owner As OperationContext) Implements IExtension(Of System.ServiceModel.OperationContext).Attach
End Sub
Public Sub Detach(owner As OperationContext) Implements IExtension(Of System.ServiceModel.OperationContext).Detach
End Sub
End Class
EDIT:
When I say that data is sticking around, I mean that when I call Current in a new service call I expect the Extensions list to be empty (in the code below in the Current() property), but there is always an existing instance of CustomOperationContextExtension there already that is left over fro a previous call. I'm not sure under which circumstances this happens.

Can I modify the Request.Headers collection?

I have an ASP.NET site that uses a third-party reporting component. This component is misbehaving by throwing a NullReferenceException whenever the client browser is not specifying a User-Agent in the request headers.
It's basically an odd scenario that I'm just trying to come up with a workaround for. I do not know who/what client is not specifying a User-Agent, which seems like bad form IMO, but we have to deal with the exceptions it is generating. I have logged a support ticket with the third-party regarding the bug in their reporting component, but I have my doubts about how fruitful that route is going to be. So my thought was just to detect when the User-Agent is blank and default it to something just to appease the reporting component. However, I can't seem to change anything in the Request.Headers collection. I get the following exception:
Operation is not supported on this platform.
I'm starting to believe I'm not going to be able to do this. I understand why ASP.NET wouldn't allow this, but I haven't come up with any other workaround.
Update: At penfold's suggestion, I tried to add the User-Agent to the Request.Headers collection using an HttpModule. This got it added to the Headers collection, but did nothing to update the Request.UserAgent property, which is what is causing the reporting component to fail. I've been looking through .NET Reflector to determine how that property is set so that I can update it, but I haven't come up with anything yet (there isn't just a private field that drives the property that I can find).
Recently I also facing similar problem same as you. I overcome the problem
of Request.UserAgent by using a mock HttpWorkerRequest.
(Assuming you already solve the agent string in Request.Headers with custom HttpModule)
Here is the sample code:
Friend Class MockedRequestWorker
Inherits HttpWorkerRequest
Private ReadOnly _BaseHttpWorkerRequest As HttpWorkerRequest
Private ReadOnly _UserAgent As String
Friend Sub New(ByVal base As HttpWorkerRequest,
ByVal UserAgent As String)
_BaseHttpWorkerRequest = base
_UserAgent = UserAgent
End Sub
Public Overrides Sub EndOfRequest()
_BaseHttpWorkerRequest.EndOfRequest()
End Sub
Public Overrides Sub FlushResponse(ByVal finalFlush As Boolean)
_BaseHttpWorkerRequest.FlushResponse(finalFlush)
End Sub
'Note: remember to override all other virtual functions by direct invoke functions
'from _BaseHttpWorkerRequest, except the following function
Public Overrides Function GetKnownRequestHeader(ByVal index As Integer) As String
'if user is requesting the user agent value, we return the
'override user agent string
If index = HttpWorkerRequest.HeaderUserAgent Then
Return _UserAgent
End If
Return _BaseHttpWorkerRequest.GetKnownRequestHeader(index)
End Function
End Class
then, in your custom HttpApplication.BeginRequest handler, do this
Private Sub BeginRequestHandler(ByVal sender As Object, ByVal e As EventArgs)
Dim request As HttpRequest = HttpRequest.Current.Request
Dim HttpRequest_wrField As FieldInfo = GetType(HttpRequest).GetField("_wr", BindingFlags.Instance Or BindingFlags.NonPublic)
Dim ua As String = "your agent string here"
Dim _wr As HttpWorkerRequest = HttpRequest_wrField.GetValue(request)
Dim mock As New MockedRequestWorker(_wr, ua)
'Replace the internal field with our mocked instance
HttpRequest_wrField.SetValue(request, mock)
End Sub
Note: this method still does not replace the user agent value in ServerVariables, but it should able to solve what you need(and my problem too)
Hope this help :)
protected void Application_BeginRequest(Object sender, EventArgs e) {
const string ua = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)";
Request.Headers["User-Agent"] = ua;
var httpWorkerRequestField = Request.GetType().GetField("_wr", BindingFlags.Instance | BindingFlags.NonPublic);
if (httpWorkerRequestField != null) {
var httpWorkerRequest = httpWorkerRequestField.GetValue(Request);
var knownRequestHeadersField = httpWorkerRequest.GetType().GetField("_knownRequestHeaders", BindingFlags.Instance | BindingFlags.NonPublic);
if (knownRequestHeadersField != null) {
string[] knownRequestHeaders = (string[])knownRequestHeadersField.GetValue(httpWorkerRequest);
knownRequestHeaders[39] = ua;
}
}
}
I think the best way of handling this is to use a http module that will check the header and inject the user agent if necessary.
As you have found out you cannot use the set method on the Headers object. Instead you will have to inject the user agent string into the header via protected properties that can be accessed through reflection as outlined in the answer to this question.
UPDATE
Unfortunately Request.UserAgent doesn't use the information held in Request.Headers, instead it calls the method GetKnownRequestHeader in HttpWorkerRequest. This is an abstract class and from looking at the decompiled library code the actual implementation varies depending on the hosting environment. Therefore I cannot see a way to replace the user agent string in a reliable manner via reflection. You could roll your own WorkerRequest class but for the amount of effort I don't think the payoff would be worth it.
Sorry to be negative but I think its just not possible to set the user agent within the web application in a simple manner. Your best option at the moment would be to perform a pre-check for a user agent, and if the request doesn't have one return a browser not supported error message.
You could also investigate injecting something earlier on, i.e. in IIS or at your proxy server if you use one.
Also I would recommend that this issue is reported to SAP. I know they are actively working on the Viewer at the moment and who knows they might fix it and maybe even add support for Opera!

Instantiating a class within WCF

I'm writing a WCF WebMethod to upload files to, of which I taken snippets from around the web. The WCF interface looks like this:
<ServiceContract()>
Public Interface ITransferService
<OperationContract()>
Sub UploadFile(ByVal request As RemoteFileInfo)
End Interface
<MessageContract()>
Public Class RemoteFileInfo
Implements IDisposable
<MessageHeader(MustUnderstand:=True)>
Public FileName As String
<MessageHeader(MustUnderstand:=True)>
Public Length As Long
<MessageBodyMember(Order:=1)>
Public FileByteStream As System.IO.Stream
Public Sub Dispose() Implements IDisposable.Dispose
If FileByteStream IsNot Nothing Then
FileByteStream.Close()
FileByteStream = Nothing
End If
End Sub
End Class
Within ASP.NET, when the web method is consumed, for some reason it only works when the interface is used as part of the instantiation of RemoteFileInfo:
Protected Sub btn_Click(sender As Object, e As EventArgs) Handles btn.Click
If fu.HasFile Then
Dim fi As New System.IO.FileInfo(fu.PostedFile.FileName)
' this is the line in question --------------
Dim cu As ServiceReference1.ITransferService = New ServiceReference1.TransferServiceClient()
' -------------------------------------------
Dim uri As New ServiceReference1.RemoteFileInfo()
Using stream As New System.IO.FileStream(fu.PostedFile.FileName, IO.FileMode.Open, IO.FileAccess.Read)
uri.FileName = fu.FileName
uri.Length = fi.Length
uri.FileByteStream = stream
cu.UploadFile(uri)
End Using
End If
End Sub
Can anyone advise why it is not possible to create an instance of TransferService using the following approach:
Dim cu As New ServiceReference1.TransferServiceClient()
If I try the above, it breaks this line:
cu.UploadFile(uri)
...and UploadFile must be called with three parameters (FileName, Length, FileByteStream) even there is no method that uses this signature.
Why is the Interface required when creating an instance of this class please?
When you create the proxy for your service with the "Add Service Reference" dialog, by default the proxy creation code will "unwrap" message contracts, like the one you have. If you want the message contract to appear as you defined on the server side on your proxy, you need to select the "Advanced" tab, and check the "Always generate message contracts" option. With that you'll get the message contract in your client as well.
The issue is that when a MessageContract is encountered as a parameter, the WCF client generation assumes by default that you want to implement a messaging-style interface, and provides the discrete properties from the message contract as part of the client-side interface.
The Using Messaging Contracts article in MSDN contains a very detailed description of what can be done with a messaging contract and I suspect that Microsoft chose this default behavior because of some of the "games" that you can play with the messages.
However, if you examine the code generated for your UploadFile on the client side, there are some interesting tidbits that help to explain what is going on.
The first is the comments for the UploadFile method in the interface:
'CODEGEN: Generating message contract since the operation UploadFile is neither RPC nor document wrapped.
...
Function UploadFile(ByVal request As ServiceReference1.RemoteFileInfo) As ServiceReference1.UploadFileResponse
This implies that the contract would have been generated differently if the message contract had a different implementation.
The second is that you will see that there is nothing special about the code that is used to actually make the service call:
Public Sub UploadFile(ByVal FileName As String, ByVal Length As Long, ByVal FileByteStream As System.IO.Stream)
Dim inValue As ServiceReference1.RemoteFileInfo = New ServiceReference1.RemoteFileInfo()
inValue.FileName = FileName
inValue.Length = Length
inValue.FileByteStream = FileByteStream
Dim retVal As ServiceReference1.UploadFileResponse = CType(Me,ServiceReference1.ITransferService).UploadFile(inValue)
End Sub
So in this case, your code is doing exactly what the generated code does. However, if the MessageContract were more complex, I suspect that this would no longer be the case.
So, for your question:
Can anyone advise why it is not possible to create an instance of
TransferService using the following approach...
There is no reason not to take this approach as long as you verify that the implementation of the method call is functionality equivalent to your code.
There are a couple of options for changing the default generation of the method in the client:
1) Remove the MessageContract attribute from the RemoteFileInfo class.
2) Although it seems to be counter-intuitive, you can check the Always generate message contracts checkbox in the Configure Service Reference Dialog Box.

ASP.NET Is it possible to handle all the null session checking in masterpage

I have the following steps in my webpage
1) User Logs in and I set the following session variables
Session("userName") = reader_login("useremail").ToString()
Session("userId") = reader_login("user_ID").ToString()
Session("firstName") = reader_login("firstName").ToString()
2) Now on my logged in VB.NET templates I reference a MasterPage called LoggedIn.Master. In Which I added the following method to check for the above null session variables. And if they null to redirect back to login page.
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
'#Check that User is Logged in, if not redirect to login page
If (Session("userId") Is Nothing) Or (Session("userName") Is Nothing) Or (Session("firstName") Is Nothing) Then
Response.Redirect(ConfigurationManager.AppSettings("site_base_url").ToString & "login/", False)
End If
3) Now my question is if I want to use any above 3 Session variables in different .net templates or usercontrols referencing the above master page do i need to AGAIN add the check
If (Session("userId") Is Nothing) Or (Session("userName") Is Nothing) Or (Session("firstName") Is Nothing) Then
Response.Redirect(ConfigurationManager.AppSettings("site_base_url").ToString & "login/", False)
End If
In the respective pages or will the check in master page do. Because at the moment i.e. if in a usercontrol I attempt to do i.e.
customerName.Text = Session("userName").ToString()
or
Response.Write(Session("userName").ToString())
I am getting the error
Object reference not set to an instance of an object.
customerName.Text = Session("userName").ToString()
You can write a wrapper around the Session to handle null values and just call the wrapper when you access the items:
Public Class SessionWrapper
Public Shared ReadOnly Property Item()
'Access session here and check for nothing
End Property
End Class
And use it like this
SessionWrapper.Item("itemName")
In answer to your question - as long as the masterpage checks the session and redirects before all your controls and page code make a reference to Session, you should be fine.
You were using OnInit() which seems reasonable, but see this article for a good understanding of the timing of events.
Incidentally, I strongly discourage the use of ad-hoc calls to Session in your page and control code. Instead, I recommend you create a static SessionManager class that does the Session referencing for you. That way, you get to benefit from strong typing, and won't be able to accidentally make hard-to-debug 'session key' typos in your code like Session["FiirstName"]. Also, you can incorporate your null-session check right into the call for the session value:
EXAMPLE (in C#, sorry!)
public static class SessionManager
{
private static void EnsureUserId()
{
if (Session["userId"] == null)
{
Response.Redirect("YourLogin.aspx", false);
}
}
public static string FirstName
{
get
{
EnsureUserId();
if (Session["firstName"] == null)
Session["firstName"] = "";
return (string)Session["firstName"];
}
set
{
Session["firstName"] = value;
}
}
}
You can create an http module that asks about the session objects and if they are null, it will redirect to the login page and by developing this http module, in each page request the module will do the check and then you can use it normally without checking.
A better way to handle this would be to add a base class for all controls that require this session variables to be present. You can then add properties to wrap access to the session and other cool stuff and the check will work even if the controls are used with a different master page.

How can I use the URL to set the current culture in ASP.NET 2.0 Web App?

I am looking in to ways to enable a site to basically have something like:
http://mysite.com/en-US/index.aspx`
Where the "en-US" can vary by culture..
This culture in the URL will then basically set the CurrentUICulture for the application..
Basically, we currently have a page where the user explicitly clicks it, but some are favouriting past that, and it is causing some issues..
I know this sort of thing is easily done in ASP.NET MVC, but how about those of us still working in 2.0? Can you guys in all your wisdom offer any suggestions/pointers/ANYTHING that may get me started? This is new to me :)
I'm sure there must be some way to pick up the request and set/bounce as appropriate.. HttpModule maybe?
Update
Just had a thought.. May be best to create VirtDirs in IIS and then pull the appropriate part from the Requested URL and set the culture in InitializeCulture?
Is storing the choice in a cookie out of the question?
Nice of you to give the users a choice but why not just default to the users client/web browser settings?
If they bookmark a page and have lost the cookie you could fall back to the default and if that is a culture you do not support then fallback further to en-US.
If you want to keep your solution you could use a rewrite engine. I've used http://www.managedfusion.com/products/url-rewriter/ in the past. For a list of engines see http://en.wikipedia.org/wiki/Rewrite_engine#IIS
You can use the routing feature developed for MVC easily with webforms. This SO question addresses doing that:
ASP.NET Routing with Web Forms
If you can't use the 3.5 framework, there are a number of URL rewriting modules out there. I have no experience with any to be able to make a recommendation.
I'm doing it in some sites with ASP.net Routing.
Here is the code:
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs on application startup
RegisterRoutes(RouteTable.Routes)
End Sub
Public Sub RegisterRoutes(ByVal routes As RouteCollection)
Dim reportRoute As Route
Dim DefaultLang As String = "es"
reportRoute = New Route("{lang}/{page}", New LangRouteHandler)
'* if you want, you can contrain the values
'reportRoute.Constraints = New RouteValueDictionary(New With {.lang = "[a-z]{2}"})
reportRoute.Defaults = New RouteValueDictionary(New With {.lang = DefaultLang, .page = "home"})
routes.Add(reportRoute)
End Sub
Then LangRouteHandler.vb class:
Public Class LangRouteHandler
Implements IRouteHandler
Public Function GetHttpHandler(ByVal requestContext As System.Web.Routing.RequestContext) As System.Web.IHttpHandler _
Implements System.Web.Routing.IRouteHandler.GetHttpHandler
'Fill the context with the route data, just in case some page needs it
For Each value In requestContext.RouteData.Values
HttpContext.Current.Items(value.Key) = value.Value
Next
Dim VirtualPath As String
VirtualPath = "~/" + requestContext.RouteData.Values("page") + ".aspx"
Dim redirectPage As IHttpHandler
redirectPage = BuildManager.CreateInstanceFromVirtualPath(VirtualPath, GetType(Page))
Return redirectPage
End Function
End Class
Finally I use the default.aspx in the root to redirect to the default lang used in the browser list.
Maybe this can be done with the route.Defaults, but don't work inside Visual Studio (maybe it works in the server)
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim DefaultLang As String = "es"
Dim SupportedLangs As String() = {"en", "es"}
Dim BrowserLang As String = Mid(Request.UserLanguages(0).ToString(), 1, 2).ToLower
If SupportedLangs.Contains(BrowserLang) Then DefaultLang = BrowserLang
Response.Redirect(DefaultLang + "/")
End Sub
Some sources:
* Mike Ormond's blog
* Chris Cavanagh’s Blog
* MSDN
Just try to add that parameter
http://yoursite/yourPage.aspx?lang=en-US
If you were utilizing resources files it will work automatically.
Good Luck

Resources