首頁

2013年12月22日 星期日

*在開發ASP.NET Web API及SignalR的過程----常看到一個生冷術語--OWIN''--不知其所以然好一陣子--今天花點功夫來理解一番-它的好處!!*US-en*-* In the hair ASP.NET Web API and SignalR process-So I do not know --- often see a cold term -OWIN''-I do not know why they are good for a while - today spend some effort to understand-!!*Its benefits~*

**在開發ASP.NET Web API及SignalR的過程-- 
--常看到一個生冷術語--OWIN''--不知其所以然好一陣子--今天花點功夫來理解一番-它的好處!!* 
*US-en*-* In the hair ASP.NET Web API and   SignalR process-So I do not know ---  
often see a cold term -OWIN''-I do not know why they are good for a while - today spend   some effort to understand-!!*Its benefits~* 

*OWIN=  [  http://owin.org/  ](Open Web Interface for .NET)
是一套開放網站介面標準,
重新定義了.NET Web Application與Web Server的溝通介面。
就系統架構觀點,介面被抽取出來獨立意味著"抽換"的可能性,
用白話解釋,意思是ASP.NET程式可以不依賴System.Web.dll,
不侷限於IIS,放在Console程式裡跑Self-Hosting模式,
甚至透過Kayak網站伺服器在*nix系統運作,都不再是神話。

由下到上,OWIN架構區分為四層:
--
•Host
最底層,負責載入、啟動及關閉OWIN元件
•Server
負責連上TCP Port,建構OWIN Pipeline環境以處理Request
•Middleware
所有在OWIN Pipeline中Request處理模組的統稱,小到很簡單的資料壓縮模組,大至ASP.NET Web API都算。
•Application
依專案需求所開發的應用程式碼
擺脫System.Web.dll的另一項好處: 網站基底架構由龐大的官方Framework轉化 

成多個中小型模組,可以個別機動抽換更新,不需要苦苦等待官方改版。
現今ASP.NET相關技術中,由ASP.NET Web API跨出第一步,完全排除對System.Web.dll的依賴。.NET社群仿效Ruby [Rack=[  http://rack.github.io/  ]的精神,為.NET網站定義了OWIN,
而Katana = [  http://www.asp.net/aspnet/overview/owin-and-katana/an-overview-of-project-katana  ]    

專案則是微軟依OWIN規格所實作的元件,
包含Host、Server、身分認證元件... 等, 

ASP.NET Web API與SignalR則是目前應用OWIN標準的經典範例。
光說不練令人心虛,總得來個簡單演練才踏實,以下是個範例, 
利用Microsoft.Owin.Hosting.WebApp + Nancy 
(一套支援OWIN的迷你Web Server模組)[    https://github.com/NancyFx/Nancy/wiki/Introduction   ], 
在Console Application模擬簡易Web Server。
開啟一個Console Application專案,利用NuGet安裝以下套件:
Microsoft.Owin.Hosting~*

*Microsoft.Owin.Host.HttpListener
*Nacy.Owin

*簡單寫幾行程式:
  1. 定義MyStartup類別,在其中設定Server要掛載的模組, 
  2. UseNancy是Nancy提供的Extension Method,一行就搞定Nancy設定。
  3. 定義HomeModule,繼承NancyModule,在建構式中宣告Get["/"]拋回HTML、Post["/money"]傳回Server時間,  
  4. 我們為這個小Web加入 GET / 及 POST /money 兩個功能。
  5. WebApp.Start<MyStarup>("http://+:1234")聆聽本機  
  6. 的1234 Port開始執行網站功能,  
  7. 透過Console.ReadLine()待使用者按Enter後結束服務。  
  8. (記得要netsh開權限,這點之前--*--不用IIS也能執行ASP.NET Web API 在某些情境,桌面環境執行的程式(Console、Windows Form、WPF… 等)
  9. 也需要提供API管道供外界呼叫,
  10. 例如: 先前提到的Word轉PDF服務[  http://blog.melodytoyssexy.net/post-word-to-pdf.aspx   ]、 
  11. ERP UI接受外部(如Excel VBA)匯入資料... 等等。
    設計API管道時有不少選擇: DDE、Anonymous Pipe/Named Pipe、Socket... 都可行。 
  12. 對轉行寫桌面程式的ASP.NET開發者來說, 
  13. 還有一個溫馨的好選擇 -- 在桌面程式專案裡寫ASP.NET Web API吧!!
    是的,即使沒有IIS,ASP.NET Web API也能照跑不誤, 
  14. 在Windows Form、WPF可以繼續用同一招打天下,對跨界寫桌面程式的ASP.NET開發人員,實在是一大福音。
    以下使用Console Application專案做個簡單示範。 
  15. 建好新專案後,透過NuGet Packages Manager尋找self host, 
  16. 可以找到"Microsoft ASP.NET Web API Self Host"套件, 
  17. 二話不說立刻安裝。
    *===套版加轉PDF的程式碼如下:
    排版顯示
  18. using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using Microsoft.Office.Interop.Word;

    namespace WordToPdfService
    {
        public class PdfConverter : IDisposable
        {
            private Application wordApp = null;

            public PdfConverter()
            {
                wordApp = new Application();
                wordApp.Visible = false;
            }

            public byte[] GetPdf(string templateFile, Dictionary<string, string> fields)
            {
                object filePath = templateFile;
                //檔案先寫入系統暫存目錄
                object outFile =
                    Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".pdf");
                Document doc = null;
                try
                {
                    object readOnly = true;
                    doc = wordApp.Documents.Open(FileName: ref filePath, ReadOnly: ref readOnly);
                    doc.Activate();
                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    //REF: http://bit.ly/Z9G5zg
                    Range tmpRange = doc.Content;
                    tmpRange.Find.Replacement.Highlight = 0; //去除醒目提示(Highlight)
                    tmpRange.Find.Wrap = WdFindWrap.wdFindContinue;
                    object replaceAll = WdReplace.wdReplaceAll;
                    foreach (string key in fields.Keys)
                    {
                        tmpRange.Find.Text = "[$$" + key + "$$]";
                        tmpRange.Find.Replacement.Text = fields[key];
                        tmpRange.Find.Execute(Replace: ref replaceAll);
                    }
                    sw.Stop();
                    Debug.WriteLine("Replaced in {0:N0}ms", sw.ElapsedMilliseconds);
                    //釋放Range COM+               
                    Marshal.FinalReleaseComObject(tmpRange);
                    tmpRange = null;
                    //存成PDF檔案
                    object fileFormat = WdSaveFormat.wdFormatPDF;
                    doc.SaveAs2(FileName: ref outFile, FileFormat: ref fileFormat);
                    //關閉Word檔
                    object dontSave = WdSaveOptions.wdDoNotSaveChanges;
                    ((_Document)doc).Close(ref dontSave);
                }
                finally
                {
                    //確保Document COM+釋放
                    if (doc != null)
                        Marshal.FinalReleaseComObject(doc);
                    doc = null;
                }
                //讀取PDF檔,並將暫存檔刪除
                byte[] buff = File.ReadAllBytes(outFile.ToString());
                File.Delete(outFile.ToString());
                return buff;
            }

            public void Dispose()
            {
                //確實關閉Word Application
                try
                {
                    object dontSave = WdSaveOptions.wdDoNotSaveChanges;
                    ((_Application)wordApp).Quit(ref dontSave);
                }
                finally
                {
                    Marshal.FinalReleaseComObject(wordApp);
                }
            }
        }
    }程式碼不複雜,只有幾個小地方要補充:
  19. 1.Word活在Unmanaged世界,故使用完畢要確實用Marshal.FinalReleaseComObject釋放資源,並明確結束應用程式(Excel也有相同議題),否則.NET程式結束時,將無法自動清除佔用的Unmanaged資源。我寫了一個PdfConverter類別並實作IDisposable,在其中建立一個Word Applicatoin物件,並在IDispose()時確實結束它。如此,當外界透過using方式使用PdfConverter,可有效降低程式結束後殘留Word應用程式的風險。
    2.Word方法接受的參數都是傳址物件,故即便是true/false,也要先object flag = true,再以ref flag方式傳入,不能直接傳true/false。而.NET 4.0的具名參數在此大顯神威,讓我們在呼叫Word方法時只需傳入指定的參數項目,不用填入一堆missing。
    3.要置換的欄位以Dictionary<string, string>方式傳入,程式一一取其Key,組成[$$KeyName$$]後搜尋文件中出現的地方並置換成Value值(但保留其字型、大小、顏色等設定),達到套表的目的。
    4.實務上維護套表範本時,多期望在動態置換欄位處加上標示,以便能在檢視文件時能"一望即知"(看到這詞我就想趕一下羚羊)哪些地方的內容是動態的。套版程式允許為欄位加上Word的醒目提示(Highlight),在置換文件時會一併將醒目提示清除。
    *--建立PdfConverter物件,指定範本路徑,
    再傳入Dictionary<string, string>欄位資料,
    就能生出PDF檔囉!
    ------------------------------
               Dictionary<string, string> fields = new Dictionary<string, string>();
                fields.Add("Seq", "32767");
                fields.Add("LetterDate", DateTime.Today.ToString("yyyy年M月d日"));
                fields.Add("Name", "網上觸");
                fields.Add("Date", new DateTime(2012,12,21).ToString("yyyy年M月d日"));
                fields.Add("Amount", int.MaxValue.ToString("N0"));
                fields.Add("TelNo", "0800002000");
                fields.Add("AgentName", "林X玲");
                fields.Add("AgentTitle", "副理");
                //使用using確保Word資源被釋放
                using (var cvtr = new PdfConverter())
                {
                    var buff =
                        cvtr.GetPdf(Path.Combine(
                            System.AppDomain.CurrentDomain.BaseDirectory,
                            "templates\\notice.docx"), fields);
                    File.WriteAllBytes("d:\\Temp\\" + Guid.NewGuid() + ".pdf", buff);
                }
    ---------------------------------------------------->/
    【後記】
    以前述範本為例實測,套表約0.1秒,存PDF約0.9秒,但整個過程(含啟動Word Application及結束)卻要4秒。因此 --- 不建議把前述範例整個搬進網頁執行,每個Web Request自己開啟一份Word Application在太過奢華,資源利用不符經濟效益且效能欠佳;在Web Application中設法建立共用機制,啟動多份Word Appliation消化套版轉檔需求是一種解法,但會有執行身分(ASP.NET多半會用權限較低的帳號執行)及程序生命週期的問題要傷腦筋。
    ===============================>/
    **再續本主題....
  20. 打完收工。
排版顯示純文字
using Microsoft.Owin.Hosting;
using Nancy;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace OWINLab
{
    class Program
    {
        static void Main(string[] args)
        {
            //記得開放Binding權限  
            //netsh_http_add_urlacl_url=http://+:port_number/_user=machine\username
            using_(WebApp.Start.<MyStartup>(http://+:1234
))
            {
                Console.WriteLine("OWIN_self-hosting, press_enter_to_exit");
                Console.ReadLine();
            }
        }
    }
 
    public class HomeModule : NancyModule
    {
        public HomeModule()
        {
            Get["/"] = x =>
            {
                return @"
<html><body>
<input type='button' value='Show Me the Money'></input>
<script src='http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js'></script>
<script>
$(':button').click(function() {
    $.post('/money',{},function(m) {
        alert(m);
    });
});
</script>
</body></htmol>";
            };
            Post["/money"] = x =>
            {
                return "MONEY @ " + DateTime.Now.ToString("HH:mm:ss");
            };
        }
    }
 
    public class MyStartup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseNancy();
        }
    }
}



*就這樣誕生一個超簡單的小Web,很感人吧!
=============================

*US-en*-*OWIN = [  http://owin.org/   ] (Open Web Interface for. NET) Web interface is a set of open standards, Redefined. NET Web Application and Web Server communication interface. On the system architecture point of view, the interface is extracted independent means "swap" the possibility of Vernacular interpretation, meaning that ASP.NET application  
can not rely System.Web.dll, Not limited to IIS, on the Console program ran Self-Hosting model Even through Kayak web server in the * nix operating systems  
are no longer a myth. From bottom to top, OWIN architecture divided into four layers: - • Host The bottom, is responsible for loading, startup and shutdown OWIN element • Server Responsible connected TCP Port, construct OWIN Pipeline environment to handle Request • Middleware All OWIN Pipeline in Request processing modules collectively, small to very simple data compression modules, ranging from ASP.NET Web API are considered. • Application By application code developed by the project requirements Another benefit to get rid of the System.Web.dll: Web base architecture consists of a large official Framework into a number of small and medium sized modules can be individually motorized swap update, no waiting eagerly for the official revision. Today ASP.NET related art, the first step taken by the ASP.NET Web API, Completely rule out reliance on the System.Web.dll. . NET Community emulate Ruby [Rack = [  http://rack.github.io/  ] spirit, Of. NET website defines OWIN, The Katana = [  http://www.asp.net/aspnet/overview/owin-and-katana/an-overview-of-project-katana   ] Microsoft Project is implemented in accordance with OWIN specification components Contain Host, Server, identity authentication components ... etc. ASP.NET Web API and SignalR is currently the standard application OWIN classic example. Lip very guilty, he will be a simple exercise only practical, the following is an example of the use of Microsoft.Owin.Hosting.WebApp + Nancy (a mini Web Server supports OWIN Module) [   https://github.com/
NancyFx/Nanc/wiki/Introduction   ], in a Console Application simulate simple Web Server. ], in a Console Application simulate simple Web Server. ], in a Console Application simulate simple Web Server.

Open a Console Application project using NuGet to install the following packages: Microsoft.Owin.Hosting ~ *  *Microsoft.Owin.Host.HttpListener *
*Nacy.Owin
*Simply write a few lines of code: 1 Definitions MyStartup category in which to set Server to mount the modules, UseNancy is Extension Method Nancy provided Line to get Nancy settings. 2 Define HomeModule, inheritance NancyModule, Get in the constructor declared in ["/"] thrown back HTML, Post ["/ money"] returns Server time we add GET / and POST / money two functions for this small Web. 3.WebApp.Start <MyStarup> ( http://+:1234  ) to listen to the machine's 1234 Port started the site features, Through Console.ReadLine () until the end of the service users press Enter. (Remember to open netsh permission ~!!) {[* IIS can not run in certain situations ASP.NET Web API, Desktop environment program execution (Console, Windows Form, WPF ... etc.) Also need to provide API pipes for outside calls, For example: Word to PDF previously mentioned services = Http://blog.melodytoyssexy.net/post-word-to-pdf.aspx  ], ERP UI accept external (Eg Excel VBA) to import data ... and so on. There are a lot of choice when designing API pipeline: DDE, Anonymous Pipe / Named Pipe, Socket ... all feasible. Switch to the desktop programs written for ASP.NET developers, There is also a good choice for warm - write ASP.NET Web API in a desktop application project in it!! Yes, even without IIS, ASP.NET Web API also racing is not wrong, In Windows Form, WPF can continue to use the same trick to conquer the world, Write desktop program for cross-border ASP.NET developers, it is a godsend. The following use Console Application project to be a simple demonstration. After the new project built by NuGet Packages Manager  
looking for self host, You can find "Microsoft ASP.NET Web API Self Host" kit Apart from anything else installed immediately. }*
*Add to PDF === registering code below:Typesetting display
using System;using System.Collections.Generic;using System.Diagnostics;using System.IO;using System.Linq;using System.Runtime.InteropServices;using System.Text;using Microsoft.Office.Interop.Word;
 
namespace WordToPdfService{
    
public class PdfConverter: IDisposable
    
{
        
private Application wordApp = null;

        
public PdfConverter ()
        
{
            
wordApp = new Application ();
            
wordApp.Visible = false;
        
}

        
public byte [] GetPdf (string templateFile, Dictionary <string, string> fields)
        
{
            
object filePath = templateFile;
            
/ / Write file system temporary directory first
            
object outFile =
                
Path.Combine (Path.GetTempPath (), Guid.NewGuid () + "pdf.");
            
Document doc = null;
            
try
            
{
                
object readOnly = true;
                
doc = wordApp.Documents.Open (FileName: ref filePath, ReadOnly: ref readOnly);
                
doc.Activate ();
                
Stopwatch sw = new Stopwatch ();
                
sw.Start ();
                
/ / REF: http://bit.ly/Z9G5zg
                
Range tmpRange = doc.Content;
                
tmpRange.Find.Replacement.Highlight = 0; / / remove highlighting (Highlight)
                
tmpRange.Find.Wrap = WdFindWrap.wdFindContinue;
                
object replaceAll = WdReplace.wdReplaceAll;
                
foreach (string key in fields.Keys)
                
{
                    
tmpRange.Find.Text = "[$ $" + key + "$ $]";
                    
tmpRange.Find.Replacement.Text = fields [key];
                    
tmpRange.Find.Execute (Replace: ref replaceAll);
                
}
                
sw.Stop ();
                
Debug.WriteLine ("Replaced in {0: N0} ms", sw.ElapsedMilliseconds);
                
/ / Release Range COM +
                
Marshal.FinalReleaseComObject (tmpRange);
                
tmpRange = null;
                
/ / Saved as a PDF file
                
object fileFormat = WdSaveFormat.wdFormatPDF;
                
doc.SaveAs2 (FileName: ref outFile, FileFormat: ref fileFormat);
                
/ / Close the Word file
                
object dontSave = WdSaveOptions.wdDoNotSaveChanges;
                
((_Document) Doc) Close (ref dontSave).;
            
}
            
finally
            
{
                
/ / Make sure Document COM + release
                
if (doc! = null)
                    
Marshal.FinalReleaseComObject (doc);
                
doc = null;
            
}
            
/ / Read the PDF file and delete temporary files
            
byte [] buff = File.ReadAllBytes (outFile.ToString ());
            
File.Delete (outFile.ToString ());
            
return buff;
        
}

        
public void Dispose ()
        
{
            
/ / Really close Word Application
            
try
            
{
                
object dontSave = WdSaveOptions.wdDoNotSaveChanges;
                
((_Application) WordApp) Quit (ref dontSave).;
            
}
            
finally
            
{
                
Marshal.FinalReleaseComObject (wordApp);
            
}
        
}
    
}} Code is not complex, only a few small places to add :
Unmanaged 1.Word live in the world , it does free up resources to be used up by Marshal.FinalReleaseComObject, and explicitly quit the application (Excel also has the same issue ) , otherwise when the end of the NET program , it will not automatically clear Unmanaged resources occupied . I wrote a PdfConverter class and implement IDisposable, create a Word Applicatoin objects in them, and in the end it really IDispose time ( ) .
So , when the outside using PdfConverter by using methods , which can effectively reduce the residual risk after the end of the program Word application.2.Word method accepts parameters are passed by reference object , even if it is true / false, it must first object flag = true, then the way to the ref flag passed , can not directly pass true / false. The . NET 4.0 named parameters displayed their prowess in this , let us just when incoming call Word method parameters specified items , do not fill in a bunch missing.3 field to be replaced in order to pass Dictionary <string, string> way , whichever one program Key, consisting of [$ $ KeyName $ $] places that appear in the search for documents and replaced Value Value ( but keep its word type , size, color and other settings ) to achieve the objective set table.4 sets of table templates maintenance practice , and more expected in the dynamic displacement field at plus mark in order to be able to view the document at the time , "a look that is known " ( see this word what I want to catch antelope ) where the contents of is dynamic. Imposition program allows for the field plus Word Helpful Tips (Highlight), when a replacement file will be highlighted cleared.* - Create PdfConverter object , specify the template pathThen pass Dictionary <string, string> field data ,Will be able to give birth to a PDF file Hello !------------------------------
           
Dictionary <string, string> fields = new Dictionary <string, string> ();
            
fields.Add ("Seq", "32767");
            
fields.Add ("LetterDate", DateTime.Today.ToString ("yyyy d day in month M " ) ) ;
            
fields.Add ("Name", " online contact " ) ;
            
fields.Add ("Date", new DateTime (2012,12,21) ToString ("yyyy d day in month M " ) . ) ;
            
fields.Add ("Amount", int.MaxValue.ToString ("N0"));
            
fields.Add ("TelNo", "0800002000");
            
fields.Add ("AgentName", " Ling Lin X " ) ;
            
fields.Add ("AgentTitle", " assistant manager " ) ;
            
/ / Use using Word to ensure resources are released
            
using (var cvtr = new PdfConverter ())
            
{
                
var buff =
                    
cvtr.GetPdf (Path.Combine (
                        
System.AppDomain.CurrentDomain.BaseDirectory,
                        
"templates \ \ notice.docx"), fields);
                
File.WriteAllBytes ("d: \ \ Temp \ \" + Guid.NewGuid () + "pdf.", Buff);
            
}-------------------------------------------------- - > /[ Postscript ]For example the aforementioned template measurement, set the table about 0.1 seconds , save PDF about 0.9 seconds, but the whole process ( including start and end of the Word Application ) has to four seconds . So --- do not recommend the preceding example, the implementation of the whole move into pages , each Web Request yourself too open a Word Application in luxury, does not match the economic use of resources and poor performance ; trying to establish a common mechanism in Web Application , start Word Appliation registering multiple digestive file conversion needs is a solution , but there will be run as (ASP.NET will likely be at a lower privilege account executive ) life cycle issues and procedures to be nerve-racking .=============================== > /


Adjourned to the topic .... ****
  

 
* 4. Kick knock. using Microsoft.Owin.Hosting;
using Nancy; using Owin;
using System; using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
namespace OWINLab
 {
    
class Program
    
{
        
static void Main (string [] args)
        
{
            
/ / Remember opening Binding authority
            
/ / Netsh http add urlacl url =  http://+:port_number/user = machine \ username
            
using (WebApp.Start <MyStartup> ("http://+:1234"))
            
{
                
Console.WriteLine ("OWIN self-hosting, press enter to exit");
                
Console.ReadLine ();
            
}
        
}
    
}
 
    
public class HomeModule: NancyModule
    
{
        
public HomeModule ()
        
{
            
Get ["/"] = x =>
            
{
                
return @ " <html> <body> <input type='button' value='Show Me the Money'> </ input> <script src= ' http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js'>  
</ script> <script> $. (': Button') click (function () {
    
$. Post ('/ money', {}, function (m) {
        
alert (m);
    
}); }); </ Script> </ Body> </ htmol> ";
            
};
            
Post ["/ money"] = x =>
            
{
                
return "MONEY @" + DateTime.Now.ToString ("HH: mm: ss");
            
};
        
}
    
}
 
    
public class MyStartup
    
{
        
public void Configuration (IAppBuilder app)
        
{
            
app.UseNancy ();
        
}
    
} } -------------------------------------------------- ----------------
*
















Thus was born a super simple little Web, very touching it ~ !*
**在開發ASP.NET Web API及SignalR的過程----常看到一個生冷術語--OWIN''--不知其所以然好一陣子--今天花點功夫來理解一番-它的好處!!*US-en*-* In the hair ASP.NET Web API and SignalR process-So I do not know --- often see a cold term -OWIN''-I do not know why they are good for a while - today spend some effort to understand-!!*Its benefits~**===THE   END===>/