Best way to distinguish between Test and Production with Classic ASP - asp-classic

I have inherited a classic ASP application. There are various things that need tidying-up but I am forced to do things gradually (I can't do a wholesale change of every script).
There are places in the system with hard-coded urls. Some scripts have to be changed before promoting to live so that the test web root name is changed to the live web root name. I am looking at ways to avoid this (essentially working out the server programmatically).
It isn't difficult. A simple check on Request("SERVER_NAME") and this sort of thing:
appName = Request.ServerVariables("SCRIPT_NAME")
i = InStr(2, appName, "/") 'keep initial "/"
If i > 0 Then
appName = Left(appName, i)
End If
This in an "everywhere-included" script will do the trick. Then just set up a global variable to hold the full "http(s)://server/app/" or a function to MakeUrlAbsolute() and off you go.
However, this smells a bit suspect to me. Wouldn't this be better stored at application level? This suggests setting it up in Global.asa, something like:
Sub Application_OnStart()
Application("WebRoot") = ...
End Sub
But now I can't get hold of the SERVER_NAME so I'm reduced to having 2 different Global.asa files (one per environment).
Is this my only choice? Work it out on every request or hard-code in Application_OnStart?
Perhaps using Session_OnStart would be a compromise. Or is there some clever trick to access information in Application_OnStart? Perhaps people go with the hard-coding because Global.asa doesn't change often.

my method for ADO Connection looks like this.
'Servername for Application Root Drive
Select Case Left(Request.ServerVariables("PATH_TRANSLATED"), 1)
Case "c" strSrvr = "80.212.207.211" 'my remote server ip
Case Else strSrvr = "(local)" 'local Sql instance (my project on D: drive)
End Select
strConn = "Driver={SQL Server};Server="& strSrvr &";uid=DBUSER;pwd=MYPASS;database=MYDATABASE"
Set objConn = Server.CreateObject("ADODB.Connection")
objConn.Open(strConn)

Related

Getting the session data from ASP classic isn't working

I am currently tasked with moving one of our older bits of software to a new server. The old server is running 2008 and the new server is on 2019. The code is a mixture of ASP and ASP.NET, both using VB. Unfortunately, I'm a C# developer and my VB knowledge is slight.
The main bit of the code is the older and is all in ASP and works fine on the new server. For a particular set of customers there is an add-on that is more recent and uses ASP.NET. For the new section of code to get the details of the logged in user it uses the code given in this answer. Unfortunately it seems like it is this bit of code that is failing.
We have this bit of code in our site.master.vb
ctx1 = ctx.Request.Url.Scheme
ctx2 = ctx.Request.Url.Host
dom = ctx1 + "://" + ctx2
Dim ASPRequest As HttpWebRequest = WebRequest.Create(New Uri(dom + "/arc/asp2netbridge.asp?sessVar=" + sessionValue))
ASPRequest.ContentType = "text/html"
ASPRequest.Credentials = CredentialCache.DefaultCredentials
If (ASPRequest.CookieContainer Is Nothing) Then
ASPRequest.CookieContainer = New CookieContainer()
End If
The asp2netbridge.asp file is stored in the directory one up from the directory that contains the code and the directory structure looks the same on both servers. The contents of the as2netbridge file are the the same as in the example code linked above with the addition of some extra comments.
It then calls a Stored Procedure on our database with the customer ID from session that should return the customer details as XML, but instead we get a 'Root Element is missing' Error. If I change the Stored Procedure to hard code the customer ID in it, rather than as a parameter then it works as expected.
Is there anything that I need to install on our server to get the system working correctly? Or is there anything else I need to do to get it to work?

ASP.NET modify connectionstring at runtime

I need to change dataset connectionstrings to point to different DBs at run time.
I've looked at a number of solutions however they all seem to be related to WinForms or web application projects or other technology slightly different than what I'm using, so I haven't figured out how apply them.
The application is like a discussion. It's a web site project based on code originally written under VS2005, and there's no budget (or personal talent!) for major changes at this time. The app is written in vb.net; I can understand answers in c#. I'm working in VS2013.
The app has three typed datasets pointing to one MDF, call it "MainDB.mdf". There are dozens of tableadapters among the three datasets.
I'm going to deploy the app it as an "alpha/demo" version. I would like to use the same code base for all users, and a separate physical version of MainDB for each user, to reduce chances that the users crash each other.
The initial demo access URL will contain query string information that I can use to connect the user with the right physical database file. I should be able to identify the database name and thus the connection string parameters from the query string information (probably using replace on a generic connection string). If necessary I could use appsettings to store fully formed connection strings, however, I would like to avoid that.
I would like to be able to change the connection strings for all the datasets at the time that the entry point pages for the app are accessed.
Changing the tableadapter connection strings at each instantiation of the tableapters would require too much code change (at least a couple of hundred instantiations); I'd just make complete separate sites instead of doing that. That's the fall back position if I can't dynamically change the connectionstrings at runtime (or learn some other way to make this general scheme work).
Any suggestions on how to approach this would be appreciated.
Thanks!
UPDATE: Per comments, here is a sample instantiation of tableadapter
Public Shared Sub ClearOperCntrlIfHasThisStaff( _
varSesnID As Integer, varWrkprID As Integer)
Dim TA As GSD_DataSetTableAdapters.OPER_CNTRLTableAdapter
Dim DR As GSD_DataSet.OPER_CNTRLRow
DR = DB.GetOperCntrlRowBySesnID(varSesnID)
If IsNothing(DR) Then
Exit Sub
End If
If DR.AField = varWrkprID Then
DR.AField = -1
TA.Update(DR)
DR.AcceptChanges()
End If
End Sub
UPDATE: Below is the test code I tried in a test site to modify the connectionString in a single instantiation of a tableadapter. It feeds a simple gridview. I tried calling this from Page_Load, Page_PreLoad, ObjectDataSource_Init, and Gridview_Databind. At the concluding response.writes, the wrkNewConnString looks changed to TestDB2, and the TA.Connection.ConnectionString value looks changed to TestDB2, but the displayed gridview data is still from TestDB1. Maybe it needs to be called from somewhere else?
Sub ChangeTableAdapter()
Dim wrkNewConnStr As String = ""
Dim wrkSel As Integer
wrkSel = 2
wrkNewConnStr = wrkNewConnStr & "Data Source=.\SQLEXPRESS;"
wrkNewConnStr = wrkNewConnStr & "AttachDbFilename=D:\9000_TestSite\App_Data\TESTDB1.MDF;Integrated Security=True;User Instance=True"
Select Case wrkSel
Case 1
wrkNewConnStr = wrkNewConnStr.Replace("TESTDB1", "TESTDB1")
Case 2
wrkNewConnStr = wrkNewConnStr.Replace("TESTDB1", "TESTDB2")
Case 3
wrkNewConnStr = "Data Source=localhost; Initial Catalog=test01;"
wrkNewConnStr = wrkNewConnStr & " User ID=testuser1; Password=testuserpw1"
End Select
Try
Dim TA As New DataSetTableAdapters.NamesTableAdapter
TA.Connection.ConnectionString = wrkNewConnStr
Response.Write("1 - " & wrkNewConnStr)
Response.Write("<br/>")
Response.Write("2 - " & TA.Connection.ConnectionString)
Catch ex As Exception
Dim exmsg As String = ex.Message
Response.Write(exmsg)
End Try
End Sub
The connection string:
<add name="TestDB1ConnectionString"
connectionString="Data Source=.\SQLEXPRESS;
AttachDbFilename=D:\9000_TestSite\App_Data\TESTDB1.MDF;Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient" />
UPDATE: the following post has lots of solutions, however, they seem to focus on web application projects, that have a project file with settings, which this web site project does not.
link with possible solutions
UPDATE: this next link was brought to my attention, and in working on it I did get it to work, however, it still relies either on having a web application project (with project file) or modifying each table adapter as they are instantiated. So, while I'm not going to implement it, I believe that is the technical answer.
modifying connection strings
sorry if this answer is too late, but I have exactly the same problem and eventually came up with a solution using Reflection.
My solution was to "save" a new default value for the connection string in the settings at run time, which means any further use of the table adapter uses the the new connection string.
It should be noted the term "save" is misleading as the new value is lost when the application closes.
Have tested and worked perfectly.
public void ChangeDefaultSetting(string name, string value)
{
if (name == null)
throw new ArgumentNullException("name");
if (value == null)
throw new ArgumentNullException("value");
Assembly a = typeof({Put the name of a class in the same assembly as your settings class here}).Assembly;
Type t = a.GetType("{Put the full name of your settings class here}");
PropertyInfo propertyInfo = t.GetProperty("Default");
System.Configuration.ApplicationSettingsBase def = propertyInfo.GetValue(null) as System.Configuration.ApplicationSettingsBase;
//change the "defalt" value and save it to memory
def[name] = value;
def.Save();
}

How to use caching?

Basically, I retrieved a dataset and I want to cache it in the server memory for one month. So I don't need to call the query again and again when running the page within this month.
I did some research and think http://msdn.microsoft.com/en-us/library/system.web.caching.cacheitemremovedcallback%28v=vs.110%29.aspx is the way to do the cache, basically i modified the sub codes to fit into my application
Public Sub RemovedCallback(k As String, v As Object, r As CacheItemRemovedReason)
itemRemoved = True
reason = r
End Sub
Function AddItemToCache(cacheKey as String, ds as Dataset)
itemRemoved = False
onRemove = New CacheItemRemovedCallback(AddressOf Me.RemovedCallback)
If (IsNothing(Cache(cacheKey))) Then
Cache.Add(cacheKey, ds, Nothing, DateTime.Now.AddMonths(1), TimeSpan.Zero, CacheItemPriority.High, onRemove)
End If
End Function
There are quite a few errors in this piece of code. One of the error is for Cache(cacheKey) says that " Cache is a type and cannot be used for expression"? where did i do wrong?
Sounds like you're using IIS cache. First of all, if this is your route - if you have an assemblies that may use cache (when available), you need to create Caching assembly in which you check for HTTPcontext. If it is null - you are running outside of IIS and caching will not be available.
A good alternative is to download Enterprise Library Caching blocks if you working with framework up to 3.5. If you use FW4.0+ you use system.runtime.caching. This way cache will be available always. There is also AppFaric and some third party cache implementations but this is probably outside the scope for you.
For your error, it sounds like your identifier Cache [your code is not showing how you assign it] is actually a type. That is if you did this
If Integer Is Nothing....
What you need is to use is syntax
System.Web.Caching.Cache.Add...
Now, this is instance. So, what you can do is
Dim c as Cache = System.Web.Caching.Cache
c.Add(....

Conditional includes in Classic ASP - where the file may not exist on the server

I am currently in a situation where I have to make some additions to an application written in classic ASP using server-side JScript on IIS.
The additions that I need to make involve adding a series of includes to the server-side code to extend the application's capabilities. However, the inc files may not exist on the server in all cases, so I need the application to fall back to the existing behavior (ignore the includes) if the files do not exist, rather than generating an error.
I know that this can't be accomplished using if statements in the JScript code because of the way that SSI works, and have not come across any ways of dynamically including the code on the server side, where the files may not exist.
Does anyone know of a way to accomplish this in classic ASP? Any help would be much appreciated.
Here's a script to dynamically include asp files:
<%
' **** Dynamic ASP include v.2
function fixInclude(content)
out=""
if instr(content,"#include ")>0 then
response.write "Error: include directive not permitted!"
response.end
end if
content=replace(content,"<"&"%=","<"&"%response.write ")
pos1=instr(content,"<%")
pos2=instr(content,"%"& ">")
if pos1>0 then
before= mid(content,1,pos1-1)
before=replace(before,"""","""""")
before=replace(before,vbcrlf,""""&vbcrlf&"response.write vbcrlf&""")
before=vbcrlf & "response.write """ & before & """" &vbcrlf
middle= mid(content,pos1+2,(pos2-pos1-2))
after=mid(content,pos2+2,len(content))
out=before & middle & fixInclude(after)
else
content=replace(content,"""","""""")
content=replace(content,vbcrlf,""""&vbcrlf&"response.write vbcrlf&""")
out=vbcrlf & "response.write """ & content &""""
end if
fixInclude=out
end function
Function getMappedFileAsString(byVal strFilename)
Dim fso,td
Set fso = Server.CreateObject("Scripting.FilesystemObject")
Set ts = fso.OpenTextFile(Server.MapPath(strFilename), 1)
getMappedFileAsString = ts.ReadAll
ts.close
Set ts = nothing
Set fso = Nothing
End Function
execute (fixInclude(getMappedFileAsString("included.asp")))
%>
The last line (the one starting with "execute") is equivalent to an "include" directive, with the difference that it can be included inside an "if" statement (dynamic include).
Bye
If you are really brave, you can read the contents of the file and then Eval() it.
But you will have not real indication of line numbers if anything goes wrong in the included code.
As a potentially better alternative: Can you not create some sanity check code in global.asa to create the include files as blanks if they do not exist?
Put simply, no. Why would the files not exist? Can you not at least have empty files present?
What you could do is something like this:
Use Scripting.FileSystemObject to detect the presence of the files
Use Server.Exeecute to "include" the files, or at least execute the code.
The only problem is that the files cannot share normal program scope variables.
The solution to this turned out to be to use thomask's suggestion to include the files and to set a session variable with a reference to "me" as per http://www.aspmessageboard.com/showthread.php?t=229532 to allow me to have access to the regular program scope variables.
(I've registered because of this, but can't seem to associate my registered account with my unregistered account)

is it possible to issue dynamic include in asp-classic?

I mean, like php'h include...
something like
my_file_to_be_included = "include_me.asp"
-- >
for what I've seen so far, there are a couple of alternatives, but every one of them has some sort of shortcoming...
what I'm trying to figure out is how to make a flexible template system... without having to statically include the whole thing in a single file with a loooooong case statement...
here there are a couple of links
a solution using FileSysmemObject, just lets you include static pages
idem
yet another one
same thing from adobe
this approach uses Server.Execute
but it has some shortcomings I'd like to avoid... seems like (haven't tried yet) Server.Execute code runs in another context, so you can't use it to load a functions your are planning to use in the caller code... nasty...
same thing
I think this one is the same
this looks promising!!!
I'm not sure about it (couldn't test it yet) but it seems like this one dinamycally handles the page to a SSDI component...
any idea???
No you can't do a dyanmic include, period.
Your best shot at this is a server.execute and passing whatever state it needs via a Session variable:-
Session("callParams") = BuildMyParams() 'Creates some sort of string
Server.Execute(my_file_to_be_included)
Session.Contents.Remove("callParams")
Improved version (v2.0):
<%
' **** Dynamic ASP include v.2.0
function fixInclude(content)
out=""
if instr(content,"#include ")>0 then
response.write "Error: include directive not permitted!"
response.end
end if
content=replace(content,"<"&"%=","<"&"%response.write ")
pos1=instr(content,"<%")
pos2=instr(content,"%"& ">")
if pos1>0 then
before= mid(content,1,pos1-1)
before=replace(before,"""","""""")
before=replace(before,vbcrlf,""""&vbcrlf&"response.write vbcrlf&""")
before=vbcrlf & "response.write """ & before & """" &vbcrlf
middle= mid(content,pos1+2,(pos2-pos1-2))
after=mid(content,pos2+2,len(content))
out=before & middle & fixInclude(after)
else
content=replace(content,"""","""""")
content=replace(content,vbcrlf,""""&vbcrlf&"response.write vbcrlf&""")
out=vbcrlf & "response.write """ & content &""""
end if
fixInclude=out
end function
Function getMappedFileAsString(byVal strFilename)
Dim fso,td
Set fso = Server.CreateObject("Scripting.FilesystemObject")
Set ts = fso.OpenTextFile(Server.MapPath(strFilename), 1)
getMappedFileAsString = ts.ReadAll
ts.close
Set ts = nothing
Set fso = Nothing
End Function
execute (fixInclude(getMappedFileAsString("included.asp")))
%>
Sure you can do REAL classic asp dynamic includes. I wrote this a while back and it has opened up Classic ASP for me in a whole new way. It will do exactly what you are after, even though people seem to think it isn't possible!
Any problems just let me know.
I'm a bit rusty on classic ASP, but I'm pretty sure you can use the Server.Execute method to read in another asp page, and then carry on executing the calling page. 15Seconds had some basic stuff about it - it takes me back ...
I am building a web site where it would have been convenient to be able to use dynamic includes. The site is all ajax (no page reloads at all) and while the pure-data JSON-returning calls didn't need it, all the different html content for each different application sub-part (window/pane/area/form etc) seems best to me to be in different files.
My initial idea was to have the ajax call be back to the "central hub" main file (that kicks the application off in the first place), which would then know which sub-file to include. Simply including all the files was not workable after I realized that each call for some possibly tiny piece would have to parse all the ASP code for the entire site! And using the Execute method was not good, both in terms of speed and maintenance.
I solved the problem by making the supposed "child" pages the main pages, and including the "central hub" file in each one. Basically, it's a javascript round-trip include.
This is less costly than it seems since the whole idea of a web page is that the server responds to client requests for "the next page" all the time. The content that is being requested is defined in scope by the page being called.
The only drawback to this is that the "web pieces" of the application have to live partly split apart: most of their content in a real named .asp file, but enough of their structure and relationship defined in the main .asp file (so that, for example, a menu item in one web piece knows the name to use to call or load another web piece and how that loading should be done). In a way, though, this is still an advantage over a traditional site where each page has to know how to load every other page. Now, I can do stuff like "load this part (whether it's a whole page or otherwise) the way it wants to be loaded".
I also set it up so each part can have its own javascript and css (if only that part needs those things). Then, those files are included dynamically through javascript only the first time that part is loaded. Then if the part is loaded repeatedly it won't incur an extra overhead.
Just as an additional note. I was getting weird ASCII characters at the top of the pages that were using dynamic includes so I found that using an ADODB.Stream object to read the include file eliminated this issue.
So my updated code for the getMappedFileAsString function is as follows
Function getMappedFileAsString(byVal strFilename)
Dim fso
Set fso = CreateObject("ADODB.Stream")
fso.CharSet = "utf-8"
fso.Open
fso.LoadFromFile(Server.MapPath(strFilename))
getMappedFileAsString = fso.ReadText()
'Response.write(getMappedFileAsString)
'Response.End
Set fso = Nothing
End Function

Resources