【 ASP 編 】

1. 特記
2. 共通処理
3. bbs.asp と global.inc
4. 設定ファイルの読み込みを global.inc に実装
5. ログ表示の実装
6. ページジャンプ用のリンク作成
7. ログ書き込みの実装
8. 過去ログ処理の実装
9. ロック処理の実装

  • ASP で使用する言語は、VBScript です

  • グローバル変数は、全て大文字にします
  • ユーザ関数名は、単語の先頭を大文字にします
  • ローカル変数は、頭に意図するデータ型を示す接頭語をつけて、単語の先頭を大文字にします

  • 設定ファイルの情報は、書き込み可能な組み込みオブジェクトである Session オブジェクトを使用します
    このオブジェクトは、セッションが終了するまで有効なので、別アプリケーションで参照できます。効率的
    な使用方法は標準化し、利用方法は詳細設計で提示すべきです

  • セッション変数に格納する数値文字列は、Cint で 明示的に数値として格納すると、比較時に直接利用
    できます。演算時には自動的に数値あつかいになりますが、ASP では全てにおいて自動的な型変換を
    あてにするのは危険です ( Request.QueryString や Request.Form は 文字列 )

  • テキストファイル全体を文字列として考えた時、改行で分解すると、最後に余計な要素が一つできてしま
    います。ですから、Ubound で要素上限を見る時は -1 する必要があります
  • Perl からの移行
  • 基本的に Perl 編で作成したものを置き換えて行きます。VBSCript では
    組み込みオブジェクトにより、入力処理は必要ありません。処理はオブジェ
    クトのメソッドと関数によって実装されて行きます

  • 共通処理を収めるファイルの拡張子は、.inc を使用しますが、これは推奨
    されるだけであって別のものでもかまいません


  •   概要
    1
    http ヘッダの出力
    2
    デバッグ情報出力関数
    3
    文字列出力関数


  • ASPにおける、デフォルトの文字列出力は Response.Write です


  • bbs.asp
  • <!-- #include file ="global.inc" -->
    <!-- #include file ="model.inc" -->
    <%
     
    LOG_VIEW = ""
    PAGE_LINK = ""
    Session("page") = 1
     
    if Request.ServerVariables("REQUEST_METHOD") = "POST" then
    	WriteLog()
    end if
     
    %>
    <!-- #include file ="head.inc" -->
    <!-- #include file ="input.inc" -->
    <%
     
    LoadLog()
    Out LOG_VIEW
     
    %>
    <!-- #include file ="foot.inc" -->
    
  • head.inc と input.inc と foot.inc は、基本的に HTML タグのみを記述します。ASP では、
    ヒアドキュメントは使用できません。<% と %> でスクリプトを埋め込んでいます
  • global.inc
  • <%
    Response.ExpiresAbsolute=#May 31,2000 23:59:59#
    Response.AddHeader "Content-Type", "text/html; Charset=Shift_JIS"
     
    Set objFs = Server.CreateObject("Scripting.FileSystemObject")
     
    ' **********************************************************
    ' デバッグ情報出力
    ' **********************************************************
    Sub DispDebug( )
     
    	Dim I
     
    	Out "<TABLE border=0><TR><TD valign=top>"
    	Out "<TABLE border=0 cellspacing=1 bgcolor=black>"
    	Out "<TR><TH bgcolor=silver colspan=2>GET</TH></TR>"
    	Out "<TR><TH bgcolor=silver>Key</TH><TH bgcolor=silver>Value</TH></TR>"
     
    	For Each strKey in Request.QueryString
    		Out "<TR>"
    		Out "<TD bgcolor=white>" & strKey & "</TD>"
    		Out "<TD bgcolor=white>" & Request.QueryString(strKey) & "</TD>"
    		Out "</TR>"
    	Next
    	Out "</TABLE>"
     
    	Out "</TD><TD valign=top>"
     
    	Out "<TABLE border=0 cellspacing=1 bgcolor=black>"
    	Out "<TR><TH bgcolor=silver colspan=2>POST</TH></TR>"
    	Out "<TR><TH bgcolor=silver>Key</TH><TH bgcolor=silver>Value</TH></TR>"
    	For Each strKey in Request.Form
    		Out "<TR>"
    		Out "<TD bgcolor=white>" & strKey & "</TD>"
    		Out "<TD bgcolor=white>" & Request.Form(strKey) & "</TD>"
    		Out "</TR>"
    	Next
    	Out "</TABLE>"
     
    	Out "</TD><TD valign=top>"
     
    	Out "<TABLE border=0 cellspacing=1 bgcolor=black>"
    	Out "<TR><TH bgcolor=silver colspan=2>SESSION</TH></TR>"
    	Out "<TR><TH bgcolor=silver>Key</TH><TH bgcolor=silver>Value</TH></TR>"
    	For Each strKey in Session.Contents
    		Out "<TR>"
    		Out "<TD bgcolor=white>" & strKey & "</TD>"
    		Out "<TD bgcolor=white>" & Session(strKey) & "</TD>"
    		Out "</TR>"
    	Next
    	Out "</TABLE>"
    	Out "</TD></TR></TABLE>"
     
    End Sub
     
    ' **********************************************************
    ' 文字列出力
    ' **********************************************************
    sub Out( strValue )
     
    	Response.Write strValue
     
    End Sub
     
    ' **********************************************************
    ' 改行付き文字列出力
    ' **********************************************************
    sub OutCr( strValue )
     
    	Response.Write strValue & vbCrLf
     
    End Sub
     
    ' **********************************************************
    ' 配列にファイルの行を全て読み込む
    ' **********************************************************
    Function GetFile( strPath )
     
    	Dim aFile
     
    	if not objFs.FileExists( strPath ) then
    		aFile = Array()
    		GetFile = aFile
    		Exit Function
    	end if
     
    	Set objFp = objFs.OpenTextFile(strPath, 1)
    	if objFp.AtEndOfStream then
    		objFp.Close
    		aFile = Array()
    		GetFile = aFile
    		Exit Function
    	end if
     
    	aFile = Split( objFp.ReadAll, vbCrLf )
    	objFp.Close
     
    	GetFile = aFile
     
    End Function
     
    ' **********************************************************
    ' 配列の範囲指定の書き込み
    ' **********************************************************
    Sub WriteArray( objFp, nStart, nEnd, aData )
     
    	Dim I
     
    	For I = nStart to nEnd
    		objFp.WriteLine aData(I)
    	Next
     
    End Sub
    %>
    

  • 設定ファイルから読み込んだ情報を、Session 変数へ格納しています

  • GetIni
     
    ' **********************************************************
    ' 設定ファイルの読み込み
    ' **********************************************************
    Sub GetIni( )
     
    	Dim I,strTarget,aIni,aFile
     
    	aFile = GetFile( Server.MapPath("bbs.ini") )
     
    	For I = 0 to Ubound( aFile )
     
    		strTarget = aFile(I)
     
    		' 空行は無視
    		if strTarget <> "" then
    			' コメント文字
    			if Mid( strTarget, 1, 1 ) <> ";" then
    				' 行を区切り文字で分解
    				aIni = Split( strTarget, "=" )
    				Session(aIni(0)) = aIni(1)
    			end if
    		End if
     
    	Next
     
    End Sub
    

  • ログ表示は、掲示板としてもっとも重要な部分であり、アプリケーションとしては「問い合せ」の分類に入
    ります。これだけでもアプリケーションとして有用性が存在します

  • 通常、アプリケーションの開発では、入力処理と表示処理は別々である事があります。そのような場合
    データは、手動で作成されます。この場合も、テキストエディタで6件のデータを作成します
  • 日付,ユーザ,タイトル,本文1<br>改行
    日付,ユーザ,タイトル,本文2<br>改行
    日付,ユーザ,タイトル,本文3<br>改行
    日付,ユーザ,タイトル,本文4<br>改行
    日付,ユーザ,タイトル,本文5<br>改行
    日付,ユーザ,タイトル,本文6<br>改行
    
  • model.inc
  • ' **********************************************************
    ' ログ表示
    ' **********************************************************
    Sub LoadLog()
     
    	' ログファイルが存在しない場合はなにもしない
    	if not objFs.FileExists( Server.MapPath( Session("INI_LOGFILE") ) ) then
    		Exit Sub
    	end if
     
    	' カレントページの保存
    	if Request.QueryString( "page" ) <> "" then
    		Session("page") = Cint(Request.QueryString( "page" ))
    	end if
     
    	Dim aFile
     
    	' ログデータを配列として読み込み
    	aFile = GetFile( Server.MapPath( Session("INI_LOGFILE") ) )
     
    	Dim nRowParPage
     
    	' ページあたりの行数
    	nRowParPage = Cint(Session("INI_ROWPERPAGE" ))
     
    	Dim I,nPage,aRow
     
    	For I = 0 to Ubound(aFile) - 1
     
    		' ログの表示データ作成 ( 自ページ分のみ )
    		if Fix( I / nRowParPage ) + 1 = Session("page") then
    			' 行を区切り文字で分解
     
    			aRow = Split( aFile(I), "," )
     
    			' 表示データの作成
    			LOG_VIEW = LOG_VIEW & aRow(0) & " " & aRow(1) & " " & aRow(2) & "<BR>"
    			LOG_VIEW = LOG_VIEW & aRow(3)
    			LOG_VIEW = LOG_VIEW & "<HR size=1 color=silver>"
    		end if
     
    	Next
     
    End Sub
    

  • ジャンプ先は、ページの数 - 1 件必要になります。ログ表示で全行を処理するループがあるのでその中
    に記述します
  • ' ページリンクの作成
    if ( I mod nRowParPage ) = 0 then
    	nPage = Fix( I / nRowParPage ) + 1
    	' 自ページの場合
    	if nPage = Session("page") then
    		PAGE_LINK = PAGE_LINK & nPage & "&nbsp;&nbsp;"
    	' 他ページの場合
    	else
    		PAGE_LINK = PAGE_LINK & "<A href" & "='" & Request.ServerVariables("SCRIPT_NAME")
    		PAGE_LINK = PAGE_LINK & "?page=" & nPage & "'>" & nPage & "</A>&nbsp;&nbsp;"
    	end if
    end if
    
  • PAGE_LINK は、foot.inc に埋め込まれています

  • <%= PAGE_LINK %>
    </BODY>
    </HTML>
     
    <% DispDebug %>
    

  • ログの書き込みは、単純に書き込むだけにします。過去ログへの最終データの移動は、過去ログ処理
    に任せる事にして、必ず OPEN した時には 最大行数 - 1 件以下のデータになるようにします

  • テキストファイルのデータを配列にするのに、改行を区切り文字としていますので、データに改行は
    含まれていませんが、WriteLine メソッドが改行を付加します
  • ' **********************************************************
    ' ログ書き込み
    ' **********************************************************
    Sub WriteLog( )
     
    	Dim aFile
     
    	' ログデータを配列として読み込み
    	aFile = GetFile( Server.MapPath( Session("INI_LOGFILE") ) )
     
    	Dim objFp
     
    	' ログファイルを書き込みモードでオープン
    	Set objFp = objFs.CreateTextFile(Server.MapPath( Session("INI_LOGFILE") ), True)
     
    	Dim strDate
     
    	' 日付フォーマット
    	dtDate = Date
    	strDate = Year( dtDate )
    	strDate = strDate & Right( "0" & Month( dtDate ), 2 )
    	strDate = strDate & Right( "0" & Day( dtDate ), 2 )
    	strDate = strDate & Right( "0" & Hour( dtDate ), 2 )
    	strDate = strDate & Right( "0" & Minute( dtDate ), 2 )
    	strDate = strDate & Right( "0" & Second( dtDate ), 2 )
    	strDate = FormatDateTime( Now ) & " (" & WeekdayName(Weekday(Date)) & ")"
     
    	Dim strUserName,strTitle,strMessage
     
    	' カンマのコンバート
    	strUserName = Replace( Request.Form("UserName"), ",", "&#44;" )
    	strTitle = Replace( Request.Form("Title"), ",", "&#44;" )
    	strMessage = Replace( Request.Form("Message"), ",", "&#44;" )
     
    	' その他のコンバート
    	strMessage = Server.HTMLEncode( strMessage )
    	strUserName = Server.HTMLEncode( strUserName )
    	strTitle = Server.HTMLEncode( strTitle )
     
    	' 改行のコンバート
    	strMessage = Replace(strMessage, vbCrLf, "<BR>")
     
    	' POST されたデータの書き込み
    	objFp.WriteLine strDate & "," & strUserName & "," & strTitle & "," & strMessage
     
    	Call WriteArray( objFp, 0, Ubound(aFile)-1, aFile )
     
    	objFp.Close
     
    End Sub
    

  • 過去ログは、カレントログで MAX を超えた行を履歴として残し、通常別のインターフェイスで表示する
    ためのデータです

  • 過去ログファイルは、1ファイルあたりの MAX 行が決められ、その件数を超えれば新しい過去ログフ
    ァイルが作成されて使用されます。古い過去ログのデータは変更される事無く、直接メンテナンスしな
    い限り行の順序は変わる事はありません

  • 過去ログ処理のトリガは、カレントログのデータが、MAX 行の状態での投稿です。まず、カレントログを
    書き込む前に、そのチェックを行なって MAX 行に達していたら、最終行を変数に保存してカレントロ
    グを MAX - 1 件の状態にしておきます

  • WritePastLog は、WriteLog の先頭でコールして下さい

  • ' **********************************************************
    ' 過去ログ処理
    ' **********************************************************
    Sub WritePastLog( )
     
    	Dim strMoveRow,aFile
     
    	' ログデータを配列として読み込み
    	aFile = GetFile( Server.MapPath( Session("INI_LOGFILE") ) )
     
    	' MAX 行数に達していたら、最終行を取得
    	if Ubound(aFile) >= Cint(Session("INI_MAXROW")) then
    		strMoveRow = aFile(Ubound(aFile)-1)
    	else
    		Exit Sub
    	end if
     
    	Dim objFp
     
    	' 最終行以外を書き込む
    	Set objFp = objFs.CreateTextFile(Server.MapPath( Session("INI_LOGFILE") ), True)
    	Call WriteArray( objFp, 0, Ubound(aFile)-2, aFile )
    	objFp.Close
     
    End Sub
    
  • この後に、仕様編のフローをコードに直して実装します
  • 	Dim strDate,strPath
     
    	' 過去ログディレクトリが存在しない場合は作成
    	if not objFs.FolderExists( Server.MapPath( Session("INI_PASTDIR") ) ) then
    		objFs.CreateFolder( Server.MapPath( Session("INI_PASTDIR") ) )
    		' 過去ログファイルを作成
    		strDate = Year( Now )
    		strDate = strDate & Right( "0" & Month( Now ), 2 )
    		strDate = strDate & Right( "0" & Day( Now ), 2 )
    		strDate = strDate & Right( "0" & Hour( Now ), 2 )
    		strDate = strDate & Right( "0" & Minute( Now ), 2 )
    		strDate = strDate & Right( "0" & Second( Now ), 2 )
     
    		strPath = Server.MapPath( Session("INI_PASTDIR") & "\" & strDate & ".csv" )
    		Set objFp = objFs.CreateTextFile( strPath, True)
    		objFp.Close
    	end if
     
    	Dim objFolder,objFiles,objFile,strCurPast
     
    	' 過去ログディレクトリの最新ファイル名を取得
    	Set objFolder = objFs.GetFolder(Server.MapPath( Session("INI_PASTDIR") )) 
    	Set objFiles = objFolder.Files
     
    	strCurPast = ""
    	For Each objFile in objFiles
    		if objFile.Name > strFile then
    			strCurPast = objFile.Name
    		end if
    	Next
    	strCurPast = Server.MapPath( Session("INI_PASTDIR") ) & "\" & strCurPast
     
    	' 過去ログデータを配列として読み込み
    	aFile = GetFile( strCurPast )
     
    	' MAX 行になっていたら、新しいファイルを作成
    	if Ubound(aFile) >= Cint(Session("INI_PASTMAXROW")) then
    		strDate = Year( Now )
    		strDate = strDate & Right( "0" & Month( Now ), 2 )
    		strDate = strDate & Right( "0" & Day( Now ), 2 )
    		strDate = strDate & Right( "0" & Hour( Now ), 2 )
    		strDate = strDate & Right( "0" & Minute( Now ), 2 )
    		strDate = strDate & Right( "0" & Second( Now ), 2 )
     
    		strCurPast = Server.MapPath( Session("INI_PASTDIR") & "\" & strDate & ".csv" )
    		Set objFp = objFs.CreateTextFile( strCurPast, True)
    		objFp.Close
    	end if
     
    	' 過去ログデータを配列として読み込み
    	aFile = GetFile( strCurPast )
     
    	' ログファイルを書き込みモードでオープン
    	Set objFp = objFs.CreateTextFile( strCurPast, True )
     
    	' カレントログのはみ出したデータの書き込み
    	objFp.WriteLine strMoveRow
     
    	' 元々のデータを書き込み
    	Call WriteArray( objFp, 0, Ubound(aFile)-1, aFile )
     
    	objFp.Close
    

  • ロック処理はファイルの書き込みの前に行い、解除は書き込みの後に行います

  • Application.Lock を使用するので、基本的にロックの失敗はありません。最大タイムアウトまで待つ事に
    なります

  • ディレクトリ作成によるロックも可能ですが、Sleep 処理が組み込み処理として存在しないので Basp21
    をインストールするか、試した事は無いですが、WshShell オブジェクトからExec メソッドでコンソール
    プログラムを呼び出して標準出力にアクセスして実行終了が待てるかもしれません
  • ' ***********************************************************
    ' ロック関数
    ' ***********************************************************
    Function LockByApp()
     
    	Application.Lock
     
    	LockByApp = True
     
    End Function
    
  • bbs.asp の変更
  • <!-- #include file ="global.inc" -->
    <!-- #include file ="model.inc" -->
    <%
     
    LOG_VIEW = ""
    PAGE_LINK = ""
    Session("page") = 1
     
    if Request.ServerVariables("REQUEST_METHOD") = "POST" then
    	LockByApp 
    	WriteLog()
    	Application.Unlock
    end if
     
    %>
    <!-- #include file ="head.inc" -->
    <!-- #include file ="input.inc" -->
    <%
     
    LoadLog()
    Out LOG_VIEW
     
    %>
    <!-- #include file ="foot.inc" -->