顯示具有 C# 標籤的文章。 顯示所有文章
顯示具有 C# 標籤的文章。 顯示所有文章

2019年12月24日 星期二

[C#]伺服器認可通訊協定違規. Section=ResponseHeader Detail=CR 必須在 LF 之前

透過 .NET  WebRequest 寫爬蟲抓取老舊系統時,發生錯誤

伺服器認可通訊協定違規. Section=ResponseHeader Detail=CR 必須在 LF 之前


解決方式(參考MSDN):
  Web.config 中<httpWebRequest> 設定 useUnsafeHeaderParsing="true"

不過MSDN中有提到這個設定方式是有風險的:

根據預設,.NET Framework 會嚴格地強制執行 RFC 2616 以進行 URI 剖析。 某些伺服器回應可能會在禁止的欄位中包含控制字元,而這會導致 HttpWebRequest.GetResponse() 方法擲回 WebException 如果useUnsafeHeaderParsing設定為true,在此情況下,HttpWebRequest.GetResponse() 將不會擲回。不過,您的應用程式很容易受到數種形式的 URI 剖析攻擊 最佳的解決方法是變更伺服器,讓回應不包含控制字元。

2019年10月1日 星期二

[ASP.NET] ADO.NET Parameters.AddWithValue 效能問題

最近發現在一個簡單的查詢語法發現效能不佳,
經查發現問題出在SqlCommand參數化查詢使用 AddWithValue,
改善後 原查詢耗時 950 ms 調整後為 24 ms!


參考: 史丹利好熱 的相關文章:

[.NET] ADO.NET Parameters.AddWithValue 對於SqlDbType的抉擇


史丹利好熱文中提到:

AddWithValue 參數化查詢時,在DbType的選擇是採.NET資料類型來決定,若查詢參數型別與實際欄位不同時有時可能會造成額外的成本

解決方式:

不要使用AddWithValue,改用 SqlParameter,並指定 SqlDbType
範例:
            SqlCommand cmd = new SqlCommand(qs, sqlConnection);
            SqlParameter p_gen01 = new SqlParameter();
            p_gen01.SqlDbType = SqlDbType.VarChar;
            p_gen01.ParameterName = "@empno";
            p_gen01.Value ="ep0020";


若使用Dapper 指定型態方式,參考範例:
            var result = sqlConnection.Query(qs,
                                            new { empno = new DbString() {
                                                Value = "ep0020",
                                                IsAnsi = true, } });




2019年7月9日 星期二

[ASP.NET]簡易設計擴充方法範例




using System;
using System.Collections.Generic;
using System.Linq;

namespace RichieCS
{
    public static class RichieCSExtension{
        public static string SayHello(this string original){
            return "Hello~" + original ;
        }
        public static string CalBornYear(this int original){
            return string.Format( "You are born in {0}", DateTime.Now.Year - original );
        }
    }
   
    public class Program
    {
        public static void Main(string[] args)
        {
           
            var myname = "Richie";
            var myage = 18;
            Console.WriteLine(myname.SayHello());
            Console.WriteLine(myage.CalBornYear());
        }
    }
}

2017年12月12日 星期二

C# 語系對應表

System.Globalization.CultureInfo.CurrentCulture.Name 各語系的對應值:
af-ZA Afrikaans - South Africa
sq-AL Albanian - Albania
ar-DZ Arabic - Algeria
ar-BH Arabic - Bahrain
ar-EG Arabic - Egypt
ar-IQ Arabic - Iraq
ar-JO Arabic - Jordan
ar-KW Arabic - Kuwait
ar-LB Arabic - Lebanon
ar-LY Arabic - Libya
ar-MA Arabic - Morocco
ar-OM Arabic - Oman
ar-QA Arabic - Qatar
ar-SA Arabic - Saudi Arabia
ar-SY Arabic - Syria
ar-TN Arabic - Tunisia
ar-AE Arabic - United Arab Emirates
ar-YE Arabic - Yemen
hy-AM Armenian - Armenia
Cy-az-AZ Azeri (Cyrillic) - Azerbaijan
Lt-az-AZ Azeri (Latin) - Azerbaijan
eu-ES Basque - Basque
be-BY Belarusian - Belarus
bg-BG Bulgarian - Bulgaria
ca-ES Catalan - Catalan
zh-CN Chinese - China
zh-HK Chinese - Hong Kong SAR
zh-MO Chinese - Macau SAR
zh-SG Chinese - Singapore
zh-TW Chinese - Taiwan
zh-CHS Chinese (Simplified)
zh-CHT Chinese (Traditional)
hr-HR Croatian - Croatia
cs-CZ Czech - Czech Republic
da-DK Danish - Denmark
div-MV Dhivehi - Maldives
nl-BE Dutch - Belgium
nl-NL Dutch - The Netherlands
en-AU English - Australia
en-BZ English - Belize
en-CA English - Canada
en-CB English - Caribbean
en-IE English - Ireland
en-JM English - Jamaica
en-NZ English - New Zealand
en-PH English - Philippines
en-ZA English - South Africa
en-TT English - Trinidad and Tobago
en-GB English - United Kingdom
en-US English - United States
en-ZW English - Zimbabwe
et-EE Estonian - Estonia
fo-FO Faroese - Faroe Islands
fa-IR Farsi - Iran
fi-FI Finnish - Finland
fr-BE French - Belgium
fr-CA French - Canada
fr-FR French - France
fr-LU French - Luxembourg
fr-MC French - Monaco
fr-CH French - Switzerland
gl-ES Galician - Galician
ka-GE Georgian - Georgia
de-AT German - Austria
de-DE German - Germany
de-LI German - Liechtenstein
de-LU German - Luxembourg
de-CH German - Switzerland
el-GR Greek - Greece
gu-IN Gujarati - India
he-IL Hebrew - Israel
hi-IN Hindi - India
hu-HU Hungarian - Hungary
is-IS Icelandic - Iceland
id-ID Indonesian - Indonesia
it-IT Italian - Italy
it-CH Italian - Switzerland
ja-JP Japanese - Japan
kn-IN Kannada - India
kk-KZ Kazakh - Kazakhstan
kok-IN Konkani - India
ko-KR Korean - Korea
ky-KZ Kyrgyz - Kazakhstan
lv-LV Latvian - Latvia
lt-LT Lithuanian - Lithuania
mk-MK Macedonian (FYROM)
ms-BN Malay - Brunei
ms-MY Malay - Malaysia
mr-IN Marathi - India
mn-MN Mongolian - Mongolia
nb-NO Norwegian (Bokmål) - Norway
nn-NO Norwegian (Nynorsk) - Norway
pl-PL Polish - Poland
pt-BR Portuguese - Brazil
pt-PT Portuguese - Portugal
pa-IN Punjabi - India
ro-RO Romanian - Romania
ru-RU Russian - Russia
sa-IN Sanskrit - India
Cy-sr-SP Serbian (Cyrillic) - Serbia
Lt-sr-SP Serbian (Latin) - Serbia
sk-SK Slovak - Slovakia
sl-SI Slovenian - Slovenia
es-AR Spanish - Argentina
es-BO Spanish - Bolivia
es-CL Spanish - Chile
es-CO Spanish - Colombia
es-CR Spanish - Costa Rica
es-DO Spanish - Dominican Republic
es-EC Spanish - Ecuador
es-SV Spanish - El Salvador
es-GT Spanish - Guatemala
es-HN Spanish - Honduras
es-MX Spanish - Mexico
es-NI Spanish - Nicaragua
es-PA Spanish - Panama
es-PY Spanish - Paraguay
es-PE Spanish - Peru
es-PR Spanish - Puerto Rico
es-ES Spanish - Spain
es-UY Spanish - Uruguay
es-VE Spanish - Venezuela
sw-KE Swahili - Kenya
sv-FI Swedish - Finland
sv-SE Swedish - Sweden
syr-SY Syriac - Syria
ta-IN Tamil - India
tt-RU Tatar - Russia
te-IN Telugu - India
th-TH Thai - Thailand
tr-TR Turkish - Turkey
uk-UA Ukrainian - Ukraine
ur-PK Urdu - Pakistan
Cy-uz-UZ Uzbek (Cyrillic) - Uzbekistan
Lt-uz-UZ Uzbek (Latin) - Uzbekistan
vi-VN Vietnamese - Vietnam

2017年11月2日 星期四

以C#實作Google Authenticator 產生一次性驗證碼

以C#實作Google Authenticator 產生一次性驗證碼

之前寫了自動登入到合作夥伴公司的B2B平台網站抓取每日報表,
該平台原本是透過圖形驗證碼的方式來阻擋爬蟲,但其選擇圖形其實是很單純,
難易度太低,有心人要解開其實是很簡單的,
後來該平台應該有意識到這個問題,最近更新為使用Google Authenticator的一次性密碼驗證碼TOPT ( Time-based One-time Password ) 進行二階段驗證

1.先下載Google Authenticator app:Android, iOS

2.使用該 app 掃瞄讀取B2B平台所提供的QRCode後便會新增一個自動產生一次性驗證碼的項目,驗證碼每30秒就會變動一次



不得不說這的確是增強了網站登入的防護,要給予掌聲鼓勵!

但這種方式對良善之心要運用自動登入的老百姓該怎麼辦?
難道要回到原始時代人工作業嗎?

想來想去,心有不甘!
走~!想解決方案去!!!

解決方案一:

即然我們可以看到這個驗證碼,那只要在VM 虛擬機器上掛個Android系統
再安裝Google Authenticator 來產生驗登碼,
寫個程式自動擷取該視窗驗證碼的畫面後進行OCR辦識,這樣就解決了。
這是我一開始的想法,後來也實作出來了(擷取視窗驗證碼的方式之後再寫另一篇)。

這樣的缺點是:
1.擷取畫面時如果該視窗突然被其他視窗蓋到就會失敗
2.須安裝VM,而且要一直開著…有點浪費系統資源

有沒有方式可以解決浪費資源這點呢?後來想說會不會Chrome有相關外掛呢?
這樣就能在要執行時再開啟Browser進行截取就好了
google了一下還真有耶!

但這個網站外掛並不是google所開發的,為什麼他產生的也可以適用?
這代表演算法一定是有公開
(Google Authenticator 也是程式碼開源的,有興趣可以到GitHub Google Authenticator參考 )
所以接下來就生出了更好的解決方案了:

解決方案二:

直接實作演算法(我是參考Tin Isles的javascript實作改用c#東抄西抄來實現而已...
只要有了金鑰,透過演算法就可以得出一次性碼囉~
那金鑰哪來?其實上面所說的QRCode只是一個紀錄私密金鑰的載體而已:



程式碼參考如下:

參考資料:

2017年10月19日 星期四

C# 簡易電子發票資料Turnkey交換範例

 C# 簡易電子發票資料Turnkey交換範例

        雖然財政部所提供的Turnkey軟體中 tv-gateway-einv-1.2.11.jar( 2020/12/25 更新:Tunkey 2.0.2版位於 cht-gateway-einv-2.0.0.0.jar )已經有建立各版本的資料交換完整的物件,考慮到java在目前公司尚未有人可以協助維護,所以想了一個比較簡易型套用樣版的方式來執行為了簡單快速,省去物件的建模,一切從簡有些XSD中的選填元素沒有一定得上拋就直接省略。

2020/12/25 補充: XSD 可以透過工具轉成 XML,可google關鍵字  XSD to XML

樣版工具使用:Mustache sharp 可至NuGet下載

程式碼參考如下:

主程式:Program.cs

其中利用 XmlReader 物件載入xsd來進行驗證xml產出是否符合規範

電子發票資料交換xsd檔可在tv-gateway-einv-1.2.11.jar中找到(參考附註)


樣版檔:templateA0401.txt

A0401物件:InvoiceA0401.csInvoiceA0401ProductItem.cs

完整程式可至GitHub下載



附註:安裝完Turnkey程式後可在 C:\Program Files\EINVTurnkey\lib 下找到 tv-gateway-einv-1.2.11.jar ( 2020/12/25 更新:Tunkey 2.0.2版位於 cht-gateway-einv-2.0.0.0.jar )

透過解壓縮工具解開後的xsd資料夾中可發現各版本的xsd都位於此