BloggerAds

2011年11月9日 星期三

[C#]在有驗證控制項的狀態下幫Button加上confirm (OnClientClick +Validator)


如果要對一個asp:Button控制項做再次詢問的動作(ex. 確認是否刪除? ),一般來說是可以直接這樣用:


<asp:Button ID="btnSubmit" runat="server" Text="OK" onclick="btnSubmit_Click"
    CausesValidation="true" OnClientClick="return confirm('are you sure?');"/>


而在有驗證控制項的狀況下,若還是用上面的return confirm方法,可是會讓驗證控制項失效的,所以要改成下列這種用法:

<asp:Button ID="btnSubmit" runat="server" Text="OK" onclick="btnSubmit_Click"
    CausesValidation="true" OnClientClick="if (Page_ClientValidate('[驗證群組]')) { return confirm('are you sure?');}"/>

其中 [驗證群組] 要記得改成你所設定的 ValidationGroup 

2011年11月1日 星期二

Google Analytics API called by C#

這是可以透過Google Analytics API 去抓出你網站的拜訪資料的功能,主要是送一個Request,再接收XML檔案,下面分兩段說明,首先是抓回XML的部份,:
(以下內容是參考 http://www.dotblogs.com.tw/lastsecret/archive/2010/10/06/18157.aspx )
private void GetXml() {  
        //送出request的網址
        var request = (HttpWebRequest)HttpWebRequest.Create(
            //在這是取國家的訪客數,參數可參考http://code.google.com/intl/zh-TW/apis/analytics/docs/gdata/gdataReferenceDataFeed.html#maxResults
            string.Format("https://www.google.com/analytics/feeds/data?"+                        
                            "ids=ga%3A{0}&dimensions=ga%3Acountry" +
                            "&metrics=ga%3Avisits&" +
                            "start-date={1}&end-date={2}&sort=-ga%3Avisits&max-results=10",
                            "12345678",  //Google Analytics 網站的ID
                            "2011-09-24",
                            DateTime.Now.ToString("yyyy-MM-dd")));

        //在header必須帶入token,token的取得請看下方的方法
        request.Headers.Add("Authorization", "GoogleLogin auth=" + GetAuthToken());

        try {
            var response = request.GetResponse();

            var responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd();

            //抓到XML, 存到暫存資料夾
            StreamWriter rd = File.CreateText(Server.MapPath("~/TempFile") + @"\Analytics_Country.xml");
            rd.Write(responseContent);
            rd.Flush();
        } catch (Exception ex) {
            Javascript.Alert(this.Page, "Error:" + ex.ToString());
        }
}

private static string GetAuthToken() {
        var postContent = string.Format(@"accountType=GOOGLE&Email={0}&Passwd={1}&service=analytics&source={2}",
                                         "Account", //Google 同號
                                         "PWD", //Google 密碼
                                         "1234"//隨便
                                        );

        byte[] bPostData = Encoding.ASCII.GetBytes(postContent);

        //參數與用法
        //參考http://code.google.com/intl/zh-TW/apis/accounts/docs/AuthForInstalledApps.html
        HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("https://www.google.com/accounts/ClientLogin");
        req.Method = "POST";
        req.ContentType = "application/x-www-form-urlencoded";
        req.ContentLength = bPostData.Length;

        var reqStream = new StreamWriter(req.GetRequestStream());

        reqStream.Write(postContent);
        reqStream.Flush();

        HttpWebResponse rep = (HttpWebResponse)req.GetResponse();
        StreamReader repStream = new StreamReader(rep.GetResponseStream(), Encoding.UTF8);
        string ctx = repStream.ReadToEnd();

        //送出的response抓取token的值
        return Regex.Split(ctx, "Auth=", RegexOptions.IgnoreCase)[1];
     
    }  
再來是抓XML中的資料:
(以下內容是參考http://www.reimers.dk/jacob-reimers-blog/added-google-analytics-reader-for-net)
protected void GetData(object sender, EventArgs e) {
        //讀XML檔案
        XDocument xmlDoc = XDocument.Load(Server.MapPath("~/TempFile") + "/Analytics_Country.xml");
        XNamespace defaultSpace = xmlDoc.Root.GetDefaultNamespace();
        XNamespace dxpSpace = xmlDoc.Root.GetNamespaceOfPrefix("dxp");
     
        //LINQ  XML    
        var datas = from entry in xmlDoc.Root.Descendants(defaultSpace + "entry")
                    select new {
                        Country = entry.Element(dxpSpace + "dimension").Attribute("value").Value,
                        Value = entry.Element(dxpSpace + "metric").Attribute("value").Value
                    };
     
        foreach (var entry in datas) {          
            Response.Write("Country:" + entry.Country);
            Response.Write("Visits: " + entry.Value + "<br />");
        }
        //datas.ToDictionary<string, int>(m => m.Country, m => m.Value);
    }

2011年8月29日 星期一

ASP.NET的WebService + autocomplete + tagit 合體

在使用Google+、Facebook時,會常常看到Tag這個東西,這東西在使用上,很方便,而且長的還蠻好看的,我很喜歡這操作很直覺的東西,所以上網找了些plugin,並拿來實作,而我找了 tagit 這個plugin,還算簡單使用(而且我認為這是我找的 plugin 當中最漂亮的...)。


一般的使用很簡單,照著 tagit 的 example就可以用了,但是一般的tag通常都會加上自動完成(AutoComplete)功能,所以不加個AutoComplete感覺就很弱,下面就提供個範例是用C#的WebService來實作AutoComplete功能(PHP很簡單,下面會提到一點):


1.在WebService部份,我們用DataTable來傳回傳,所以可以在asmx.cs這樣寫:
[WebMethod]
public DataTable GetList(string prefixText) {  
        DataTable dt = new DataTable();
        //從資料庫查詢資料的語法,請自發揮...     
        // ......略
        return dt;    //最後return一個DataTable
}
2.在aspx部份,用一個TextBox:
<asp:TextBox ID="txtTag"  runat="server" autocomplete="off"  />
3.再來就是JavaScript的實作了,通常是在document.ready時實作
$(document).ready(function() {
    $("#<% =txtTag.ClientID %>").tagit({
        allowSpaces: true,
        tagSource: function(request, response) {
            $.ajax({
                url: "/GetList.asmx/GetList", //步驟1的WebService網址及Function名稱
                data: { prefixText: request.term }, //要傳入WebService Function的變數,這裡是用來抓user所輸入的內容
                dataType: "xml",  //dataType設成Xml,php直接用json,再搭配json_encode來產生資料就可以了
                type: "POST",
                contentType: "application/x-www-form-urlencoded; charset=utf-8",    //這裡很重要,要把contentType改成WebService預設回傳的application/x-www-form-urlencoded
                dataFilter: function(data) { return data; },
                success: function(data) {    //成功抓到資料後,要執行的動作
                    response($.map($(data).find("[欄位名稱]"), function(item) {  //因為是XML所以用這個方式來取資料,[欄位名稱]請記得更改成DataTable的欄位名稱
                        return { value: $(item).text() }
                    }));
                },
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    alert(textStatus + ":" + errorThrown);  //Debug用
                }
            });
        }         
    });
4.AutoComplete的部份完成!


5.如果要再龜毛點,限制使用者必須輸入DataTable中的內容的話,就在JavaScript中先加入這一段:
$.expr[':'].textEquals = function(a, i, m) {
        return $(a).text().match("^" + m[3] + "$");
};
6.再來改一下document.ready中tagit的部份,加入onTagAdded 的事件。


7.最後還要為tagit所產生的text加入change事件。


6+7後的JavaScript如下:
$(document).ready(function() {
    $("#<%=txtTag.ClientID %>").tagit({
        allowSpaces: true,
        tagSource: function(request, response) {
            $.ajax({
                url: "/GetList.asmx/GetList",
                data: { prefixText: request.term },
                dataType: "xml",
                type: "POST",
                contentType: "application/x-www-form-urlencoded; charset=utf-8",
                dataFilter: function(data) { return data; },
                success: function(data) {
                    response($.map($(data).find("[欄位名稱]"), function(item) {
                        return { value: $(item).text() }
                    }));
                },
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    alert(textStatus + ":" + errorThrown);
                }
            });
        },
        onTagAdded: function(event, tag) {  
            //注意!別在這alert東西,不然會執行兩次
            //.ui-autocomplete、tagit-label 是tagit執行後預設的class
            if ($(".ui-autocomplete li:textEquals('" + tag.find(".tagit-label").text() + "')").size() == 0) {
                that.removeTag(tag);    //若輸入的內容不在資料庫中,則移除tag
            }
        }
    });


    $(".tagit input").change(function() {
        if ($(".tagit li:textEquals('" + $(this).val() + "')").size() == 0) {
            $(this).val('');  //若輸入的內容不在資料庫中,則清空TextBox的內容
        }
    });
});
8. 再次大功告成


參考資料:
http://stackoverflow.com/questions/2587378/how-to-implement-mustmatch-and-selectfirst-in-jquery-ui-autocomplete
https://github.com/aehlke/tag-it
http://jqueryui.com/demos/autocomplete/

UserControl 提供事件給網頁(Parent Page)觸發的方式

最近在弄一個讓 UserControl 提供事件給網頁呼叫的東西下面就提供一個範例,來讓UserControl中的 GridView的 RowSelecting 事件提供給網頁(Parent Page)觸發

1.首先,在UserControl的ascx.cs中,宣告一個事件:
public event EventHandler PublicRowSelecting;
2.再來還是在UserControl的ascx.cs中,讓原本的GridView RowSelecting的事件,執行剛剛宣告的事件PublicRowSelecting:
protected void gvMain_RowSelecting(object sender, GridViewSelectEventArgs e) {                             
            this.PublicRowSelecting(this, new EventArgs());       
}
3.接下來就是在網頁中使用了,在網頁(Parent Page)的aspx.cs 直接設定事件
UserControl1.PublicRowSelecting += new EventHandler(UserControl1_PublicRowSelecting);
4. UserControl1_PublicRowSelecting 是你要執行的程式,這個範例只要 Response.Write就好了

protected void UserControl1_PublicRowSelecting(object sender, EventArgs e) {
      Response.Write("Test OK");
}
5.大功告成,接下來只要執行到 UserControl中GridView的RowSelecting事件,就會執行UserControl1_PublicRowSelecting了。

2011年8月23日 星期二

.Net Web Service Internal Server Error 錯誤處理方式


在.Net 使用Web Service的方式很簡單,只要使用下列code就可以使用

首先在aspx加上如下程式:
<asp:ScriptManager runat="server" ID="ScriptManager1">
    <Services>
        <asp:ServiceReference Path="~/AAA.asmx" />
    </Services>
</asp:ScriptManager>
JavaScript即可用下列方式使用:
Class名稱.function名稱(參數, 完成時執行的function名稱, 錯誤時執行的function名稱, TimeOut時執行的function名稱);
ex.
AAA.funAAA(strB, OnComplete, OnError, OnTimeOut)

但若出現「Internal Server Error」,往往會讓人摸不著頭緒,很難找原因。在這提供幾個參考的debug方式:

1.先檢查程式中Web Service的路徑(URL)是否有錯誤,相對路徑與絕對路徑的使用是否都抓到asmx檔案。

2.Web Service是否有正確回傳資料,可直接打上網址 (ex. http://localhost/AAA.asmx) 並直接輸入參數檢查資料是否正確,或是否有錯誤發生。

3.若WebService 無法用遠端存取,只可在本機(Local)存取 ,則要在web.config加上下列藍色的部份:
<configuration>
    <system.web>
        <webServices>
            <protocols>
                <add name="HttpPost"/>
                <add name="HttpGet"/>
            </protocols>
        </webServices>

    </system.web>
</configuration>

2011年8月16日 星期二

ASP.NET Page Life Cycle 圖

最近在找一些有關ASP.NET Page的 Life Cycle的資料,看到了這張MSDN提供的圖(應該是Lag了很久...),覺得這張圖表達的很清楚,所以貼上來,當作自已的備忘錄,參考看看。


轉載資料:http://blogs.msdn.com/b/aspnetue/archive/2010/01/14/asp-net-page-life-cycle-diagram.aspx

2011年7月27日 星期三

C#將DataTable做Group By並取SUM、AVG、COUNT的方法(不用LINQ)

LINQ是個很方便的東西,但在比較舊的環境(.Net Framerwork 3.0以下的版本),是沒辦法使用LINQ的,這時要將DataTable做Group By可是很麻煩的一件事,下面就提供範例:
DataTable dt = new DataTable();    //原來的資料
dt = Get(); //取得資料,PS. Get()是我自己寫的Function,要記得換成你取得DataTable的function

//將原來的DataTable做Distinct並複製到新DataTable,ToTable的第一個參數是設定是否
//要做Distinct,當然要設成true,其他參數是要做Group By的欄位名稱
DataTable dtGroup = dt.DefaultView.ToTable(true, "column1", "column2");

//開始加欄位
dtGroup.Columns.Add("CountColumn");
dtGroup.Columns.Add("SumColumn");
dtGroup.Columns.Add("AvgColumn");

int count = 0;

for (int i = 0; i < dtGroup.Rows.Count;i++) {
    //取資料,用String是因為上方加欄位時,沒指定型別為數字
    string strCount = dt.Select("column1='" + dtGroup.Rows[i]["column1"].ToString() + "'").Length.ToString();
    string strSum = dt.Compute("SUM(column3)", "column1='" + dtGroup.Rows[i]["column1"].ToString() + "'").ToString();
    string strAvg = dt.Compute("AVG(column4)", "column1='" + dtGroup.Rows[i]["column1"].ToString() + "'").ToString();

    //設定資料
    dtGroup.Rows[i]["CountColumn"] = (strCount == "" ? "0" : strCount);
    dtGroup.Rows[i]["SumColumn"] = (strSum == "" ? "0" : strSum);
    dtGroup.Rows[i]["AvgColumn"] = (strAvg  == "" ? "0" : strAvg );       
}
最後dtGroup就是Group By後的資料了。

用jQuery取得RadioButtonList所選的值

開發.NET程式時,常常會使用到RadioButtonList控制項,在很多狀況下常需要依照使用者所選的值去做一些判斷或動作,雖然.Net本身就有OnSelectedIndexChanged事件可以用,搭配UpdatePanel等,可以做到不用PostBack的功能,但用jQuery也是可以寫的很簡單。

若使用的是RadioButtonList控制項,aspx內容如下:

<asp:RadioButtonList ID="rblTest" runat="server" >
    <asp:ListItem Text="1" Value="1" Selected="True" />
    <asp:ListItem Text="2" Value="2" />           
</asp:RadioButtonList>
產生的Html語法會長成這個樣子:
<table id="rblTest" border="0">
<tr>
<td><input id="rblTest_0" type="radio" name="rblTest" value="1" checked="checked" /><label for="rblTest_0">1</label></td>
</tr><tr>
<td><input id="rblTest_1" type="radio" name="rblTest" value="2" /><label for="rblTest_1">2</label></td>
</tr>
</table>
有看到吧? 產生的input物件,name 都是 rbTest,但id卻是以rbTest_開頭,再加上從0開始的序號,所以如果要使用jQuery取得所選的值的話,可以有兩種方法:
1.用id開頭取得:
$("input[id^='<% =rblTest.ClientID %>']:checked").val()
2.用name取得:
$("input[name='<% =rblTest.ClientID %>']:checked").val()
兩種都可以試試。

2011年7月15日 星期五

MySQL使用暫存table時出現Error Code: 1137 Can't reopen table: 'XXX'錯誤

在MySQL使用暫存Table時,若出現 Error Code: 1137 Can't reopen table: 'XXX' 的錯誤
表示你使用了像下列的語法:
CREATE TEMPORARY TABLE tempTable
SELECT * FROM tableA

INSERT INTO tempTable
SELECT * FROM tableB
WHERE tableB.`cname` IN (SELECT `cname` FROM tempTable);
MySQL中,是不允許一段語法裡,做出像是遞迴的動作的語法,其他資料庫我沒試過,所以不知道可不可以這樣做,而會出現這種錯誤,通常都是因為疏忽(就是在說我自已…= =),建議解決方式:再用一個暫存Table來存原本暫存Table的值,如下:
CREATE TEMPORARY TABLE tempTable
SELECT * FROM tableA

CREATE TEMPORARY TABLE tempTable2
SELECT * FROM tempTable

INSERT INTO tempTable
SELECT * FROM tableB
WHERE tableB.`cname` IN (SELECT `cname` FROM tempTable2);

當 $(document).ready 碰上UpdatePanel

在 ASP.NET的開發中,UpdatePanel 是一個讓人又愛又恨的存在,讓人很方便使用,卻又常會造成一些問題。其中一個問題就是當UpdatePanel的(Partial) PostBack觸發後,你會發現你在$(document).ready中所設定的事件、屬性、ccs…等,全部都消失了,畫面並沒有reload,但你設定的動畫、mouseover、mouseout、watermark…等,卻都失效,沒有作用了。下面就是為了應對這種問題的解決方式:
$(document).ready(function() {
     pageLoad();
   
     Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function() {
            pageLoad();
     });
});

function pageLoad() {
    //你要執行的程式碼
}
加入Sys.WebForms.PageRequestManager.getInstance().add_endRequest 這個事件,並重新執行一次原本$(document).ready所執行的程式就可以解決這個問題了。

如果你要在UpdatePanel的(Partial) PostBack執行前,去處理一些事件,像是記錄當下的數值之類的,你可以用 Sys.WebForms.PageRequestManager.getInstance().add_beginRequest這個事件來做處理。

如果你在$(document).ready中有設定css的class,這個方式在Chrome、Firefox等瀏覽器下,也可以確實的解決問題。但在IE下,有時你會發現,不管你怎麼重新設定css class,好像都沒作用,javascript事件、function、甚至用$(selector).css()來設定css,全部都有跑,但唯獨設定css class的部份卻跟沒跑一樣。

如果你的css class是寫在網頁中,那這個問題的解決方式有二:
1.將你的css屬性,全改用$(selector).css()來設定。
2.將你class的style區段移至<head>中,這樣就可以解決問題了,真是莫明奇妙...

參考資料:
http://stackoverflow.com/questions/256195/jquery-document-ready-and-updatepanels
http://stackoverflow.com/questions/1732164/losing-css-class-added-through-jquery-on-updatepanel-partial-postback
http://sudheerkondraguntaprogramming.blogspot.com/2011/05/issue-with-css-stylesheet-and.html

2011年7月1日 星期五

jQuery UI Modal Dialog 中ASP:Button 無法PostBack的解決方式

如果有在.NET網頁(aspx)中使用jQuery UI 的Modal Dialog Plugin的人,應該會發現,若將ASP:Button控制項放在Dialog中,裡面的Button按下去是不會PostBack的,google了一下後,發現只要加一行程式就可以解決了,解決方式如下:
$("#btnA").click(function() {                             
    $("#divDialog").dialog({    //顯示Modal Dialog
        title: 'title',
        modal: true
    });
    //重點在這
    $("#divDialog").parent().appendTo($("form:first")); 
});          

$("#btnCancel").click(function() {
    $("#divDialog").dialog("close");   //關閉Modal Dialog
});

參考資料:
http://stackoverflow.com/questions/568784/jquery-modal-form-dialog-postback-problems

2011年6月25日 星期六

Google Map JavaScript API v3 實作滑鼠右鍵(Right Click)選單

要實作Google Map的右鍵選單,可參考下列做法,在實作之前,先做好Google Map API的一些基本動作,不熟的人可以參考之前的文章1. Google Map  初始化這段。接著就可以參考下面的動作了。


首先可以先設定一個CSS的Class,如下:
.contextMenu {
       position: absolute;
       min-width: 100px;
       z-index: 1000;  /* 必要 */
       background: #fff;
       border-top: solid 1px #CCC;
       border-left: solid 1px #CCC;
       border-bottom: solid 1px #676767;
       border-right: solid 1px #676767;
       padding: 0px;
       margin: 0px;
       display:none;    /* 預設為不顯示 */
       font-size:smaller;
}
其中有個很重要的一點,就是z-index這個屬性一定要給,如果沒給的話,不管你怎麼按右鍵、改程式,選單都不會出來。原因的話,我還沒查到...= = (這是心路歷程)。

再來可以寫一個DIV,就是要在滑鼠右鍵按下( Right Click)時顯示的選單,把class設剛剛寫好的CSS Class:
<div id="rightMenu" class="contextMenu">
    <a>設為起點</a>    
    <a>設為終點</a>   
</div>
在接下來的JavaScript部份,因為有使用jQuery語法,所以必須要include jQuery,請在<head>中加入這一行程式:
<script type="text/javascript" src="jquery-1.6.1.min.js"></script>
接下來就是JavaScript部份了:
<script language='javascript'>
    var contextMenu = $("#rightMenu");    //宣告物件
    $(map.getDiv()).append(contextMenu);          
  
    //加入事件
    google.maps.event.addListener(map, "rightclick", function(e) {     
        //先隱藏div
        contextMenu.hide();

        var mapDiv = $(map.getDiv());
        var x = e.pixel.x;
        var y = e.pixel.y;              

        //當選單太靠近邊界時,調整選單位置
        if (x > mapDiv.width() - contextMenu.width()) {
            x -= contextMenu.width();
        }
        if (y > mapDiv.height() - contextMenu.height()) {
            y -= contextMenu.height();
        }
        //設定座標,並加入動畫
        contextMenu.css({ top: y, left: x }).fadeIn(100);                    
    });

    //當下列事件觸發時,隱藏選單
    $.each('click dragstart zoom_changed maptypeid_changed'.split(' '),  
        function(i, name) {
            google.maps.event.addListener(map, name, function() {
                contextMenu.hide()
            });
    });
</script>
參考資料:
http://www.pinsoftstudios.com/tips/google-maps-api-v3-right-click-context-menu

2011年6月22日 星期三

ASP.NET 動態新增Control、UserControl在PostBack後的處理

當有控制項(Control)或使用者控制項(UserControl)在頁面中是屬於動態新增的,若在頁面上不會觸發PostBack事件,其實是很好處理的,在使用上不會有什麼大問題。問題在於觸發PostBack的情況是很容易發生的,當PostBack發生後,你會發現所有動態產生的控制項通通不見了,這個問題實在是很惱人,所以在動態新增控制項時,要做一些特別的處理,程式才會乖乖的照著你的意思跑。

動態增加控制項的方法如下(以下以使用者控制項為例):
//載入User Control
UserControl1 ucObj=(UserControl1)LoadControl("~/UserControl1.ascx");

//若為一般控制項(Control),只要new一個新物件即可
//以TextBox為例,TextBox txtObj = new TextBox();

panel1.Controls.Add(ucObj); //加在Panel中
若上述程式放在按鈕的Click事件中,當User每按一次按鈕時,控制項照理說會一直增加,但實際上卻一直只有一個UserControl出現在畫面上。出了什麼問題? 原因就是沒有ViewState,所以PostBack後,原先增加的控制項就消失了,只有最後新增的一個控制項存在。因為是動態新增的,所以在Page_Init時.NET沒辦法將控制項重建,而解決方式就是用ViewState或Session將新增的控制項總數記下來,在Page_Load時將動態新增的控制項加回去,在新增控制項時,要多記錄控制項數目,所以上面的程式要改成如下:
//載入User Control
UserControl1 ucObj=(UserControl1)LoadControl("~/UserControl1.ascx");

panel1.Controls.Add(ucObj); //加在Panel中

if (ViewState["UcCount"] != null) {
    //記錄控制項數目
    ViewState["UcCount"]=           (int.Parse(ViewState["UcCount"].ToString())+1).ToString();
}else{
    ViewState.Add("UcCount", "1");
}
另外,在Page_Load事件中,要加入下列程式碼:
if (ViewState["UcCount"] != null) {
    for (int i = 1; i <= int.Parse(ViewState["UcCount"].ToString()); i++) {                              panel1.Controls.Add((UserControl1)LoadControl("~/UserControl1.ascx"));
    }
}
另外要注意的是,新增控制項時不可以設定ID(ex. ucObj.ID = "xxx"),否則每次PostBack時,都會視為一個新的物件,而讓PostBack前輸入的值消失。

參考資料:
http://social.msdn.microsoft.com/forums/zh-TW/236/thread/c18da009-fbcd-437b-9f9c-f758ade1e477/

2011年6月20日 星期一

ASP.NET錯誤:具有潛在危險 Request.Form 的值已從用戶端 偵測到 對應方式

在ASP.NET中,預設會將XSS (Cross Site Script)攻擊擋掉,若發現XSS攻擊,ASP.NET就會出現下列這種錯誤訊息:
具有潛在危險 Request.Form 的值已從用戶端 (XXXX) 偵測到
對於網頁開發者來說,自動擋掉錯誤是一件好事,但系統卻會出現錯給你的User看,這是個很不好的,為了避免這種狀況,
有一種解決方式是將 validateRequest 設成false,如果是針對個別網頁的話,修改的地方如下:
<%@ Page Language="C#" AutoEventWireup="false" CodeFile="Test.aspx.cs" Inherits="Test" validateRequest="False" %>
如果要針對整個網站的話,則是透過修改 web.config來達到整個網站關閉的目的
<system.web>
         <pages validateRequest="False" />
</system.web>

但若依上述方式修改,等於是讓系統開了個漏洞,所以若以上述方式修改的話,還必須要為每個控制項的值加入HtmlEncode,以防止XSS攻擊,ex.
txtBox1.Text = HttpUtility.HtmlEncode(txtBox1.Text)
總而言之,上面的方式,我個人覺得不太好,所以接下來還有另一種方式:透過Error事件,來做處理。這也有兩種方式可以實作
第一種方式,是用Page_Error來做處理,在程式中加入如下程式:
    protected void Page_Error(object sender, EventArgs e) {              
        if (Server.GetLastError().GetType().ToString() == "System.Web.HttpRequestValidationException") {
            Response.Redirect([要導入的錯誤畫面]);
        }

        Server.ClearError(); //清掉錯誤,讓ASP.NET的錯誤不出現
    }
若嫌要在每個網頁加上這段程式很麻煩,可以建立一個 BasePage 類別,再讓每個網頁繼承即可。

另外還有一種更方便的方式:在 Global.asax新增application_error事件,程式如下:
 void Application_Error(Object sender, EventArgs e)
    {
       if (Server.GetLastError().GetType().ToString() == "System.Web.HttpRequestValidationException") {
            Response.Redirect([要導入的錯誤畫面]);
        }
     
        Server.ClearError();
    }
這樣就不會出現ASP.NET錯誤了

參考資料:
http://tgw1029.blogspot.com/2010/02/requestform.html
http://social.msdn.microsoft.com/forums/zh-TW/236/thread/a4ecbacc-ffb9-49b9-bf52-93749a3e699a
http://www.dotblogs.com.tw/kirkchen/archive/2009/12/07/12314.aspx

Codeigniter 使用POST方式實作jQuery UI中的AutoComplete(自動完成)

要在Codeigniter中利用jQuery來實作AutoComplete,目前比較受歡迎的,大概是這個 jQuery Plugins jQuery UI。jQuery Plugins透過POST來實作的方式Google一下就有很多,反而jQuery UI的討論比較少。

在jQuery UI的官方網站中的範例中,是使用 $.getJSON方式來傳輸入的值給Server端,程式如下:

$.getJSON( "search.php", {term: extractLast( request.term )}, response );
$.getJSON是使用GET方式,與Codeigniter預設是不開放GET有衝突到,所以要解決這個問題有兩種做法:
一、將Codeigniter中application/config/config.php中的 $config['enable_query_strings'] 設為 true
二、不要用 $.getJSON,改用$.ajax來抓server端的資料

下面就來說說第二種改用$.ajax來抓資料的方式。

Server端的程式如下:

$aryPara = array(
   $this->input->post('term') //抓term(使用者所輸入的值)
);

$sql = "SELECT col1 FROM table1 WHERE col1 LIKE CONCAT(?, '%');";
$result = $this->db->query($sql, $aryPara);        
if($result->num_rows() > 0){       
   $data= array(); //一定要轉成有label及value的array
    
   foreach($result->result() as $row){
      //設定資料, col1替換成你的欄位名稱
      $data[] = array('label'=> $row->col1, 'value'=> $row->col1);
   }
}
echo json_encode($data);
   
$result->free_result();
$this->db->close();  

Client端的程式如下:

$("#txtKeyword").bind("keydown", function( event ) {
    if ( event.keyCode === $.ui.keyCode.TAB &&
           $(this).data("autocomplete").menu.active ) {
       event.preventDefault();     
    }
})
.autocomplete({
    source: function(request, response) {
       $.ajax({url: "search.php",
           //注意:這裡是設定post的變數及值,Server端就是以這個變數名稱取值
           data: {term: extractLast( request.term )},  
           dataType: "json",
           type: "POST",
           success: function(data){
                   response(data);
           }
       });
    },     
    search: function() {
      var term = extractLast(this.value);
      if (term.length < 2 ) { //當字數小於兩個字時,不抓資料
          return false;
      }
    },
    focus: function() {return false;},
    select: function(event, ui) {
          var terms = split( this.value );
          terms.pop();
          terms.push( ui.item.value );
          terms.push( "" );
          this.value = terms.join( ", " );
          return false;
    }
});

function split( val ) {
    return val.split( /,\s*/ );
}
function extractLast(term) {
    return split( term ).pop();
}
這樣就大功告成了。

參考:
http://forum.jquery.com/topic/jquery-autocomplete-new-parameter-implemented-method-get-post
http://api.jquery.com/jQuery.getJSON/
http://jqueryui.com/demos/autocomplete/#multiple-remote

2011年6月13日 星期一

Google Map JavaScript API v3 的一些常用語法

下列是使用Google Map API時的一些常用的語法

1. Google Map  初始化:
先 include google map api
<script type="text/javascript"src="http://maps.google.com/maps/api/js?sensor=false" ></script>
接著在Body中加入下面這個Div,這個Div是要用來顯示Google Map的物件
<div id="map_canvas" style="width:300px;height:300px;"></div>
接著加入下面的JavaScript程式碼,可以 寫在body的onload事件 或 jQuery的 $(document).ready 中
var latlng = new google.maps.LatLng(23.69781, 120.96051499999999);  //台灣座標
var myOptions = {
    zoom: 15,    //地圖縮放比例,數字越大,路越大
    center: latlng,  //地圖的中心座標
    mapTypeId: google.maps.MapTypeId.ROADMAP  //地圖型態,可參考 這裡
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
2. 新增Marker:
var myLatlng = new google.maps.LatLng(23.69781, 120.96051499999999);
var marker = new google.maps.Marker({
    position: myLatlng,    //Marker的座標
    map: map,    //map物件,以這篇文章的例子,就是上面那個map物件
    title: 'title'    //當滑鼠移至Marker上時,要顯示的Title文字
});
3. 在Marker click時,顯示InfoWindow:
google.maps.event.addListener(marker, 'click', function() {
    infowindow.open(map, marker);
});
4. 新增InfoWindow:
var infowindow = new google.maps.InfoWindow();    //初始一個物件
infowindow.setContent('這是內容');    //InfoWindow的內容,可用Html語法
infowindow.setPosition(myLatlng);    //座標
5. 在地圖Click時新增一個Marker:
google.maps.event.addListener(map, "click", function(event) {
    marker_Click = new google.maps.Marker({
                    map: map,
                    position: event.latLng
                });
            });
6. 用地址查座標:
function getLatLngByAddr() {
    var geocoder = new google.maps.Geocoder();  //定義一個Geocoder物件
    geocoder.geocode(
        { address: '[地址]' },    //設定地址的字串
        function(results, status) {    //callback function
            if (status == google.maps.GeocoderStatus.OK) {    //判斷狀態
                alert(results[0].geometry.location);             //取得座標                                
            } else {
                alert('Error');
            }
      }
 );
}

參考:
http://itunes.apple.com/us/app/wikihow-how-to-diy-survival/id309209200?mt=8
Google Map Reference
Google Map API Tutorial

2011年6月6日 星期一

Codeigniter上實現Ajax (使用jQuery)

下面這個在Codeigniter上實現Ajax的方式,是透過jQuery的post方法來實現,首先寫好一個controller,controller的程式範例如下:
<?php
class testAjax extends CI_Controller {
function index() {
}
function testPost() {
echo "result=".$this->input->post('para');
}
 function testPara($strPara) {
echo "result=".$strPara;
}
}
?>
上面有兩個function,分別是以post (testPost)、傳入參數 (testPara)兩種方式來呈現,其實只要選擇一種來實作即可,可以自已選擇自已喜歡的方式。View的程式範例如下:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="jquery-1.6.1.min.js"></script>
<title></title>
</head>
<body>
    <script language='javascript'>
    //<![CDATA[
    function testAjaxPost() {     
      var options = {'para' : '[在這輸入para的值]'};
   
    $.post('/index.php/testAjax/testPost', options, function(data) {
           alert(data);
    });
    }

    function testAjaxPara() {            
       $.post('/index.php/testAjax/testPara/[在這輸入要傳入$strPara的值]', null, function(data) {
         alert(data); 
     });
    } 
    //]]>
    </script>
 
<form id='frm1' method='post'>
<table border="0">
<tr>
<td>
<input type='button' id='btnTestPost' value='Post' onclick='testAjaxPost();' />
<input type='button' id='btnTestPara' value='Para' onclick='testAjaxPara();' />
</td>
</tr>
</table>
</form>    
</body>
</html>
這樣就可以執行了,jQuery的post說明如下:
$.post(URL [,POST_Para] [,Success] [,Fail]);

URL:Server端的URL。
POST_Para:要以Post方式傳入的陣列變數(不需使用時,可不傳入)。
Success:成功時要執行的程式碼(不需使用時,可不傳入)。
Fail:失敗時要執行的程式碼(不需使用時,可不傳入)。
其中傳入參數(testAjaxPara)方式,可以直接在網址輸入參數的值
而Post (testAjaxPost)方式則是透過javascript的陣列來傳入參數的值。

若要做比較進階的Ajax運用,可以在controller將值輸出成json或xml格式,就可以做到更多的功能

參考:http://stackoverflow.com/questions/308054/learning-how-to-use-ajax-with-codeigniter