2014年6月10日

C# Web Service遇到IE8 cross domain post問題的解決方式

近幾年太習慣用Web Service來拆解程式功能做系統架構,弄到有點Web Service上癮了。

自己最常使用的開發工具是C# with .NET framework,其中的asmx是根據soap協定來做的Web Service。而在我用asmx建立了一堆service之後,又希望可以讓前端網頁能簡單呼叫這些service,於是就做了一個簡單的soap post function。

下列是這個function內部分程式碼的節錄(其實只是一個標準的javascript client post範例):

var xmlhttp;
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else {
// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open('POST', service_uri, true);
xmlhttp.setRequestHeader('Content-Type', 'text/xml; charset=utf8');

但是這段code遇到IE8就死了,完全不會動,因為IE8有一個全世界獨有的設計:因安全性考量,XMLHttpRequest只能接受相對位址(只允許對同domain發request)。

不過cross domain的需求仍然存在,所以IE8還是留了一條後路:使用XDomainRequest。於是上面那段程式碼最後修改成了下面的樣子:

var isIE8 = window.XDomainRequest ? true : false;
var xmlhttp;

if (isIE8) {
xmlhttp = new XDomainRequest();
var protocal = service_uri.split('/')[0];
xmlhttp.open('POST', service_uri.replace(protocal, location.protocol));
} else {
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open('POST', service_uri, true);
xmlhttp.setRequestHeader('Content-Type', 'application/soap+xml; charset=utf-8');
}

在IE8的處理區段中,除了改用XDomainRequest之外,還有兩個不一樣的點:1.來源網站的protocal要跟service一樣,XDomainRequest似乎不能接受http向https,或是https向http的請求(個人實驗得出結論,不是十分肯定);2.XDomainRequst無法自訂request header,所以不能自訂Content-Type。

不能自訂header,無法設定Content-Type,XDomainRequest自以為安全認為所有的cross domain post都是text/plain,而asmx又只接受xml,沒有在header裡放個Content-Type的request根本就不被接受啊!

在經過無數的google result洗禮後,最後終算讓我找到自己覺得很爛的解決方式,這回要來修改Server端的程式了。

做法是在request被傳到asmx之前,檢查看看header裡有沒有帶Content-Type,如果沒有的話,就幫它加上去。只要在Global.asax上加入下列程式碼就好了:

protected void Application_BeginRequest(object sender, EventArgs e)
{
if (Request.RequestType == "POST" && !Request.Headers.AllKeys.Contains("Content-Type"))
{
Request.ContentType = "application/soap+xml; charset=UTF-8";
}
}

最後,花了大概五個小時的處理,總算讓我的程式能順利在IE8上運行了,我想如果早點認輸改用JQuery,可能十分鐘就處理掉了吧。

但我實在不是很能理解,為什麼我得在2014年花這麼多時間去處理一個只剩2%市佔2009年瀏覽器版本才會發生的問題啊!


read more...