すいません、VB4しかやってないんです、VBAはやったけど(ぼそ) チラシの裏だって立派な書き込み空間なんだからねっ!資源の有効活用なんだからねっ!とか偉そうに言ってるけど、実は色々と書き残したいだけ

だからなに? どうしろと? くるみサイズの脳みそしかないあやしいジャンガリアンベムスターがさすらう贖罪蹂躙(ゴシックペナルティ)

ホーム 連絡をする 同期する ( RSS 2.0 ) Login
投稿数  632  : 記事  35  : コメント  11678  : トラックバック  143

ニュース


片桐 継 は
こんなやつ

かたぎり つぐ ってよむの

大阪生まれ河内育ちなんだけど
関東に住みついちゃったの
和装着付師だったりするの
エセモノカキやってたりするの
VBが得意だったりするの
SQL文が大好きだったりするの
囲碁修行中だったりするの
ボトゲ好きだったりするの
F#かわいいよF#

正体は会った人だけ知ってるの

空気読まなくてごめんなさいなの


わんくまリンク

C#, VB.NET 掲示板
C# VB.NET掲示板

わんくま同盟
わんくま同盟Blog


WindowsでGo言語
WindowsでGo言語


ネット活動


SNSは疲れました

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

イベント活動

プログラムの活動

SSIでIISでGoでテンプレ
http://blogs.wankuma.com/esten/archive/2010/08/20/192454.aspx

のGo言語で作ったSSIカウンター、実は致命的な欠点がまだあるの。

同じ人が何回もF5すると、その回数だけ訪問回数が増えちゃうんだ。

なので、その対策として、訪問者の重複チェックもしてみよう、というのでさらにリファクタ。

package main

//使用する汎用パッケージ
import (
 fmt "fmt";
 util "io/ioutil"
 strings "strings"
 os "os"
 template "template" 
 strconv "strconv"
 time "time"
)

//テンプレートに引き渡す情報
type Record struct {
    today string
    yesterday string
    total string 
  }

//ファイルレコード分析用
const (
   Today = iota;
   Yesterday;
   Total;
   Day;
)

//データファイル名(実行exeと同じフォルダにあること)
const COUNTER_FILE = "cnt.dat"
const VISITED_FILE = "chk.dat"

//出力テンプレート
const tpl = ` 
  <div class="counter">
    <div class="ctToday">
      <div class="hToday">Today:</div>
      <div class="cToday">{today}</div>
    </div>
    <div class="ctYesterday">
      <div class="hYesterday">YesterDay:</div>
      <div class="cYesterday">{yesterday}</div>
    </div>
    <div class="ctTotal">
      <div class="hTotal">Total:</div>
      <div class="cTotal">{total}</div>
    </div>
  </div>
` 

//エラー処理
func IfErrorExit(err os.Error) {

  if err == nil {
    return
  }

  fmt.Printf("Content-Type: text/html;charset=UTF-8\n\n")
  fmt.Printf("%s\n",err.String())
  
  os.Exit(0)    

}

//重複アクセスチェック
func checkVisited(cbuf string, nowday int, inrm string) (bool,string) {

  //先頭1つめの記録日付を取り出して、チェック
  rmInfos := strings.Split(cbuf , "," ,-1)

  v,er := strconv.Atoi(rmInfos[0])
  IfErrorExit(er)

  //今日の最初のサイト訪問者なら無条件にカウントアップ
  if v != nowday {

    return false,fmt.Sprintf("%d,%s,",nowday,inrm)
    
  }
  
  //今日はじめての訪問なら無条件にカウントアップ
  if strings.Index(cbuf,  fmt.Sprintf(",%s,",inrm)) < 0 {

    return false,fmt.Sprintf("%s,%s,",cbuf,inrm)
  
  }
  
  return true,cbuf
  
}

//ファイル名を取得
func getReadFileName() (string,string ){
  //IISなので、SSIで動かす時はルート相対パス指定

  //IISからみたURLのルートパス(URLのルート物理パス)
  myrpath := strings.Split(os.Getenv("PATH_TRANSLATED"),"\\",-1)

  //IISからみた実行スクリプトパス
  myspath := strings.Split(os.Getenv("SCRIPT_NAME"),"/",-1) 

  //配列の一番後ろ=スクリプトexeファイル名なので消去
  myspath[len(myspath)-1] = "";
  
  //ルートパス+スクリプトパス+データファイル名の物理パスを返す
  ppath := fmt.Sprintf("%s%s",strings.Join(myrpath,"\\"),strings.Join(myspath,"\\"))
  return fmt.Sprintf("%s%s",ppath,COUNTER_FILE),fmt.Sprintf("%s%s",ppath,VISITED_FILE) 
}

//メイン処理
func main() {

  //読込データファイル(exeと同フォルダ内の固定ファイル)を読む
  ctFile,ckFile := getReadFileName()
  
  buf,er := util.ReadFile(ctFile)
  IfErrorExit(er)

  checkbuf,er := util.ReadFile(ckFile)
  IfErrorExit(er)

  //データレコードを分割して、順番に意味づけ
  myCounter := strings.Split(fmt.Sprintf("%s",buf),",",-1)
  
  wtotal,er := strconv.Atoi(myCounter[Total])
  IfErrorExit(er)

  wtoday,er := strconv.Atoi(myCounter[Today])
  IfErrorExit(er)

  wyesterday,er := strconv.Atoi(myCounter[Yesterday])
  IfErrorExit(er)

  wday,er := strconv.Atoi(myCounter[Day]) 
  IfErrorExit(er)

  //チェックのための日付準備
  now := time.LocalTime()
  visitInfo := os.Getenv("REMOTE_ADDR")
  
  //サイト再訪問チェック
  retbln,retstr := checkVisited(fmt.Sprintf("%s",checkbuf), now.Day ,visitInfo)
  
  //今日はじめての訪問だったら、カウントアップしてログ記録
  if retbln == false {

    //前回データ保持日付が記録されているので、比較して日付更新処理
    if now.Day != wday  {
      
      wyesterday = wtoday
      wtoday = 1
      wday = now.Day
  
    }else{
  
      wtoday++;
      
    }
    
    //トータルカウントアップ処理
    wtotal++;
        
    //データの書き込み
    er = util.WriteFile(ctFile, []byte(fmt.Sprintf("%d,%d,%d,%d",wtoday,wyesterday,wtotal,wday)),777)
    IfErrorExit(er)
    
    er = util.WriteFile(ckFile, []byte(retstr),777)
    IfErrorExit(er)
    
  }
  
  //テンプレの準備
  var nowCounter Record;
  nowCounter.today = fmt.Sprintf("%05d",wtoday);
  nowCounter.yesterday = fmt.Sprintf("%05d",wyesterday);
  nowCounter.total = fmt.Sprintf("%05d",wtotal);  

  //テンプレ書き出し
    fmt.Printf("Content-Type: text/html;charset=UTF-8\n\n")
  t := template.MustParse(tpl, nil); 
  t.Execute(nowCounter, os.Stdout); 

  os.Exit(0)
  
}

サイト訪問者のリモートアドレスを1日分蓄積して、そこと照らし合わせてカウンターまわすかどうか決めてるしくみ。

SSIなので、CookieヘッダーはWEBサーバー側で握りつぶされるので使おうと思っても使えないから、サーバー側で対処ね。ここ、意外とひっかかるので注意だったり。

今回のソースは、Go言語らしく、1関数で複数の戻り値を使って書いてるから、実行する効率はPerlやCで書く時よりも良いはず。Go言語の作者さんは「ここがすごいところなんだぜ?」とイチオチの機能だし。

といっても、まだまだきっとリファクタできるんだろけど、私はこれで満足?したから、また何かCGIとか欲しくなったら作ろう<おい

投稿日時 : 2010年8月21日 11:05

コメント

No comments posted yet.

Post Feedback

タイトル
名前
Url:
コメント