在IIS佈署asp.net應用程式小技巧

在 IIS 佈署 asp.net 應用程式基本上只要把檔案複製過去就可以,但如果沒有停止網站的話,檔案是可能會複製失敗的,這會導致佈署失敗。

第一個小技巧是使用 App_offline.htm ,在佈署位置新增 App_offline.htm 檔案以後,IIS 會自動讓網站進入維護模式,對來訪問網站的人會看到 App_offline.htm 檔案的內容。這時候 .dll 也會被卸載,就不會造成複製失敗。

第二個小技巧是 sleep,上面有提到可以新增 App_offline.htm 來讓 IIS 進入維護模式,但這需要一點點時間讓 IIS 去做出反應,那就會需要 sleep 。可是Windows 並沒有 sleep 這類的指令,所以沒辦法 sleep ,那麼可以怎麼做呢?這裡可以參考 如何在批次檔(Batch)中實現 sleep 命令讓任務暫停執行 n 秒 ,我後來是使用 ping 這個指令來作,ping 可以指定次數,也可以指定秒數,透過 ping 就可以達到 sleep 的目的。

ping -c 4 -w 1000 127.0.0.1

參考資料

在 Ubuntu 開發 .Net 應用程式

這幾年微軟大力推廣 .Net core ,把 .Net runtime 鋪到每個 Linux 發行版去,所以現在 Ubuntu、Red Hat Enterprise、Debian、Fedora、CentOS、Alpine 等 Linux 發行版都可以安裝 .Net runtime 跟 .Net SDK 。

以 Ubuntu 來說,安裝方法很簡單,就是照這篇 在Ubuntu 上安裝 .NET SDK 或 .NET 執行時間 來做就可以。

我桌機安裝的是 Ubuntu 21.10 ,所以下面就只節錄 Ubuntu 21.10 的部份

wget https://packages.microsoft.com/config/ubuntu/21.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
sudo apt-get update; \
  sudo apt-get install -y apt-transport-https && \
  sudo apt-get update && \
  sudo apt-get install -y dotnet-sdk-6.0 aspnetcore-runtime-6.0

安裝完成以後,就可以使用 dotnet 這個指令來開發應用程式了。

首先建立目錄,然後切換到此目錄下

mkdir -p ~/dotnetcore-hello
cd ~/dotnetcore-hello

然後用 dotnet 指令建立範本

dotnet new webapp -n dotnetcore-hello -o .
  • webapp 表示建立網頁應用程式。
  • -n 是表示專案名稱。
  • -o . 是表示輸出到目前的目錄。

產生完畢,就可以啟動。

dotnet run

啟動以後會有以下訊息

正在建置...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7219
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5063
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/user/dotnetcore-hello/

從訊息可以知道,用瀏覽器開啟 http://localhost:5063 或是 https://localhost:7219 就可以開啟網站。

一般在開發時,會把 port 固定起來,以方便開發;或者是禁用 https 。那這樣該怎麼做呢?這時候可以參考 How do I disable HTTPS in ASP.NET Core 2.1 + Kestrel? ,dotnet 應用程式的 port 跟 https 設定是在 Properties/launchSettings.json 裏面去指定的,只要修改裡面的 applicationUrl 即可。

例如 applicationUrl 本來是 https://localhost:7219;http://localhost:5063,改為只有 http,port 8000 並讓網路上其他電腦都可以連進來的話,就改為 http://0.0.0.0:8000

最後,開發完畢以後,要輸出檔案去佈署,可以用 dotnet publish 來建置。

dotnet publish -c release -o out

以上面的指令來說,

  • -c 是指定要使用的 configuration
  • -o 是指定把建置好的檔案輸出到 out 目錄下。

今天就把 QuickStart 的開發流程紀錄起來,免得以後遇到又在那邊查。

在 Ubuntu 上使用最新的 mono/monodevelop

Ubuntu 上的 mono/monodevelop 都不是很新,一般來說,用 openSuSE 或 archlinux 是比較恰當的,因為 mono 早期是依托在 Novell ,而 openSuSE 又是由 Novell 維護的關係。而 archlinux 則是有強大的 repository 可以選用。

當然,要自己編譯也不是不行,真的試了之後,你會發現 mono 倒是還好,但要編譯 monodevelop 的話就是挺痛苦的事情了,因為相依性太多、太麻煩。

早期 mono 是不太管其他 distro 上的,但後來 openSuSE 推了 build service 之後,mono 就利用這個服務來提供套件給幾個知名 distro (CentOS, Debian, Fedora, openSuSE) 使用。在 Ubuntu 安裝的說明可以參考這個網頁,套件名稱是 mono-opt 跟 mono-xsp-opt ,版本是目前最新的版本。安裝以後的目錄是在 /opt/mono ,主要是要跟原來預設的 mono 區隔開來。在 terminal 下使用,可以用 source /opt/mono/env.sh 來變更必要的環境變數,以確定使用到對的 mono runtime。

monodevelop 的話,可以利用 Eberhard Beilharz 的 ppa,套件名稱是 monodevelop-5、monodevelop-database-5、monodevelop-debugger-gdb-5,這個 ppa 也同時提供了 mono 3.4 ,但沒有 xsp 套件。在 monodevelop 裡有設定可以指定要使用哪個版本的 runtime,這可以在 [編輯(Edit)][Preferences] 裡的 專案(Project) > .NET 運行時期(.NET runtime) 裡找到。

基本上 openSuSE 的 build service 應該是不太會無緣無故消失,mono 會持續維護這裡,但 Eberhard Beilharz 的 ppa 就不一定了,這點在安裝、使用的時候,請多留意。

在 Ubuntu 12.04 上編譯 monodevelop (1)

環境是 12.04 LTS,想編譯 monodevelop,首要就是去 github 把源碼拉下來,拉下來以後,還要執行 git submodule update –init –recursive 更新 submodule。

需要安裝的套件有:

  • autoconf
  • automake
  • mono-gmcs
  • libgtk2.0-cil-dev
  • libmono-cil-dev
  • libgnome2.0-cil-dev
  • libgconf2.0-cil-dev
  • mono-dmcs
  • libmono-addins-cil-dev
  • libmono-addins-gui-cil-dev
  • libvala-0.12-dev
  • mono-xbuild

不囉唆,用 apt-get install 裝上就是,接著下 ./configure –profile=all –prefix=/opt/monodevelop 產生 Makefile,然後開始編譯,原本以為應該沒問題了,豈知出現錯誤:

MonoDevelop.AspNet.Mvc.Completion/RazorCompletionTesting.cs(30,26): error CS0234: The type or namespace name `Mvc’ does not exist in the namespace `MonoDevelop.AspNet’. Are you missing an assembly reference?
MonoDevelop.AspNet.Mvc.Completion/RazorCompletionTesting.cs(33,26): error CS0234: The type or namespace name `Mvc’ does not exist in the namespace `MonoDevelop.AspNet’. Are you missing an assembly reference?
MonoDevelop.AspNet.Mvc.Completion/RazorCompletionTesting.cs(35,26): error CS0234: The type or namespace name `Mvc’ does not exist in the namespace `MonoDevelop.AspNet’. Are you missing an assembly reference?
MonoDevelop.AspNet.Mvc.Completion/RazorCompletionTesting.cs(135,52): error CS0246: The type or namespace name `RazorCSharpEditorExtension’ could not be found. Are you missing a using directive or an assembly reference?
MonoDevelop.AspNet.Mvc.StateEngine/RazorParsingTests.cs(32,26): error CS0234: The type or namespace name `Mvc’ does not exist in the namespace `MonoDevelop.AspNet’. Are you missing an assembly reference?
MonoDevelop.AspNet.Mvc.Completion/RazorCompletionTesting.cs(127,48): error CS0115: `UnitTests.MonoDevelop.AspNet.Mvc.Completion.RazorTestingParser.Parse(bool, string, System.IO.TextReader, MonoDevelop.Projects.Project)’ is marked as an override but no suitable method found to override
MonoDevelop.AspNet.Mvc.Completion/RazorCompletionTesting.cs(137,67): error CS0246: The type or namespace name `RazorCSharpParsedDocument’ could not be found. Are you missing a using directive or an assembly reference?

看起來是少了 Mvc 這個 namespace,可是我已經有安裝 libmono-system-web-mvc2.0-cil 了啊,怎會這樣子?

經過追蹤以後,發現跟 Mvc 沒關係,而是編譯到 main/src/addins/AspNet/MonoDevelop.AspNet.Mvc 時,這邊說少了幾個 .dll 檔案,csprojet 裡有試著加上 ProjectReference ,可是仍然有出現錯誤。我想應該是 .csproj 問題,Makefile 裏面是寫用 xbuild 來編譯 .csproj 檔案,產出的 dmcs 指令,有很多 /reference 參數,而有問題的這幾個 .dll ,路徑都是直接寫死到 /usr/lib/monodevelop 下,真的是怪了,而在 .csproj 裡又找不到這些設定。

mono xsp and phalanger

修正策略以後,就決定先在 Windows 上安裝 mono。很快地,我安裝好 2.11 ,想不到這卻是卡關的開始,這一版的 xsp 很奇怪,老是會丟出 System.IO.FileNotFoundException ,甚至連正常的 aspx 都無法處理,所以最後就乾脆放棄了。在這段過程裡,稍微參考了以前加 boo 支援的 web.config 。

今天早上就立馬移除了 mono 2.11,改安裝 2.10.8 ,這是比較穩定的一版,為了怕跟昨天一樣卡關,就先試了 aspx,這次就可以了。然後拿之前加 boo 支援的 web.config ,參考 phalanger configuration 來修改。昨天是走馬看花,今天比較仔細看過之後,發現這網頁上提供的設定其實有些問題。我個人是這樣改的:

  1. 首先複製最下面的 local app.config/web.config 一節的設定,先存為 web.config。
  2. 修改 paths ,把裡面都註解掉,只留下 DynamicWrappers 與 Libraries,接著把 “{PATH}\” 移掉,所以 DynamicWrappers 的內容就是 dynamic,Libraries 內容就是 bin 。
  3. 修改 classLibrary ,這邊可以大刀闊斧砍光光,然後參考上面 global machine.config 裡的第3段設定,就加 PhpNetClassLibrary 跟 PhpNetXmlDom 這兩個 assembly 就可以。
  4. 把 global machine.config 裡的第1段設定跟第2段設定加到 web.config 裡,這兩段,都是在 configuration 裡。
  5. 把 global web.config 裡的設定,就是把 httpHandlers 加到 system.web 裡。
  6. 尋找 globalization ,把 value 改為 utf-8。
  7. 大功告成。

接下來,把 phalanger binaries 裡的 .dll 都放到 bin 目錄下,然後建立 dynamic 目錄,就可以開始寫你的 .php 了。

寫好以後,就打開 mono command prompt ,切換到開發目錄下,執行 xsp4 –port 8080 –root . ,以啟動 web server。再打開你的瀏覽器,在網址列輸入 http://localhost:8080/index.php 就可以了。

phalanger 會進行即時編譯,編譯好的 .dll 會放在 dynamic 目錄下。

最後放上我的 web.config:

c# webrequest二三事

前幾天遇到的一些問題跟解:

  • 要對ASP.Net網頁做POST,因為有ViewState的關係,沒辦法像一般簡單的HTML form那樣做,需要先讀取網頁之後,取出裡面的__VIEWSTATE 跟__EVENTVALIDATION ,再加上你要填的內容去重新組出網址,才可以正確地POST。參考自:C#的HttpWebRequest编程,支持带ViewState的网页POST请求通过HTTP抓包,深入理解ASP.NET WebForm ViewState
  • 需要使用帶有Cookie的HttpWebRequest,HttpWebRequest有個CookieContainer可以用,所以套上Cookie即可,參考自:WebClient 保持 Session 和 Cookie,基本上就套它的HttpClient。
  • Cookie怎麼來?除了可以透過WebBrowser控制項去撈以外,也可以用Win32 API:InternetGetCookieEx()去撈,我用WebBrowser控制項撈出來的有問題,後來就改用Win32API的InternetGetCookieEx()就行了。參考自:请问如何自动获取cookieContainer.Add()中的值Is it possible to transfer authentication from Webbrowser to WebRequest
  • utf-8轉big5的問題,轉了以後都放在字串裡,因為要拿來組網址,要使用Uri.EscapeDataString(),卻發現輸出結果一樣。沒辦法,只好自己來。首先先套轉換的過程,但是不要放回string,而是拿byte[]來用,拿來以後,取得每個byte的16進位,在前面加上 ‘%’ ,再組成字串,就可以拿來組網址。不知道有沒有什麼例外情況需要處理,又懶得去翻RFC,總之,試的結果是可以,沒問題,就先這樣用吧。

git support in Visual Studio 2010

因為用 git 管理源碼,在切換 Visual Studio 跟 Command Prompt 之間,頗為麻煩,就想說,應該是有人寫了 Visual Studio 的 git 擴充套件了吧~
進 [Tools][Extension Manager] 一找,果然已經有人寫了,主要有兩個:Git source control provider 跟 Git Extensions。Git source control provider 主要是一個給 Visual studio 用的介面,你可以設定實際上以哪個軟體去作 git,例如:msysgit、git extension、tortoise git…等等,所以容量不大,小小的。試用結果還算可以,唯一的缺點就是更動較多檔案時,會導致 Visual studio 重啟動。
Git extensions 我就沒安裝了,因為我已經裝了 msysgit。目前是以 Git source control provider 跟 Command prompt 交互使用中。

編譯DLR debug version

因為 Pro DLR 有介紹編譯Dynamic Language Runtime debug version的部份,如此一來就可以利用Visual studio來追蹤DLR內部的運作。
下載位置在這兒,目前是1.0版。
假設你是用 Visual studio 2010/Visual studio express 2010的話:

  • 如果要在 .Net 4 下debug,開啟 Src 下的 Codeplex-DLR-Dev10.sln。
  • 如果要在 .Net 2 (3.5也算)下debug,開啟 Src 下的Codeplex-DLR.sln 或 Codeplex-DLR-VSExpress.sln,開啟以後,會說要轉換。

編譯以後,就可以在 Bin 目錄下找到檔案。
本來想說真不妙,竟然得一個個Project去改Target framework,還去找了人家寫好的Visual studio macro來做,可以是後來發現reference也要改,就暈了。幸好早就有提供.Net 2.0版本的solution檔案了。

Json.Net serialization/deserialization

Json.Net 的 serialization/deserialization 其實不難,因為都內建了,我被卡到的原因,是因為我沒仔細看文件。我的類別裡有日期時間的屬性,但我一直以為不需要特別處理,但我錯了。


在用 JsonConvert.SerializeObject 時,得傳入 new IsoDateTimeConverter () 才行,要不然會導致之後 JsonConvert.DeserializeObject 時無法轉換日期時間。

var items = new List<Item>();
// other stuffs
File.WriteAllText (m_filename, JsonConvert.SerializeObject (items, Formatting.Indented, new IsoDateTimeConverter () ));
List<Item> objs = JsonConvert.DeserializeObject<List<Item>>(File.ReadAllText (m_filename));

另外一件重要的事情就是你的類別必須有 default constructor,如果沒有的話,JsonConvert.DeserializeObject 也會丟例外出來給你。

dotplurk 筆記

Plurk Api Using C# 沒有提供文件,要自己轉。不過看了原始碼以後,你會發覺轉了也沒用,基本上就是參考 Plurk API
這裡幾點是看了以後的小紀錄:

  • PlurkApi.getPlurks(),傳兩個參數跟傳三個參數是不一樣的。傳兩個參數,使用的是 Polling/getPlurks;傳三個參數,使用的是 Timeline/getPlurks
  • PlurkApi.getResponses()/PlurkApi.getAllResponse() 都是取得某噗的回應,getResponses()可以指定從第幾則開始抓;getAllResponses()則是全部抓,底層是用迴圈搭配getResponses()去抓。
  • 官方 Responses/get 會取回三個部份:friends、responses_seen、responses,PlurkApi 只會傳回 responses 這部份,所以如果要知道 response 是哪個 user,得搭配 PlurkApi.getPublicProfile() 來取得,getPublicProfile()參數是字串,傳回的是 publicProfile,publicProfile 裡有屬性:user_info,可取得 user 資訊。

另外要注意的是,Plurk API有一天 50,000 次的限制,所以使用的時候要注意避免超過限制。