[筆記]撰寫傳回 JSON 的 WebService,並透過 XDomain 提供 jQuery 跨網域呼叫

小喵最近有個需求,系統必須提供給舊系統呼叫小喵的 COM+ 元件,本來與其他系統負責人溝通後,最好能夠傳回 JSON 內容,並已經敲定透過 WebAPI 應該是比較好的一個合作方式,無奈在跟負責主機管理的人提出環境確認時,不幸得到的消息是,無法安裝新的.NET Framework,因此無法使用 WebAPI 。所幸小喵找到了一個好朋友 Donma 分享的文章,提供了解決的方法~

緣起

小喵最近有個需求,系統必須提供給舊系統呼叫小喵的 COM+ 元件,本來與其他系統負責人溝通後,最好能夠傳回 JSON 內容,並已經敲定透過 WebAPI 應該是比較好的一個合作方式,無奈在跟負責主機管理的人提出環境確認時,不幸得到的消息是,無法安裝新的.NET Framework,因此無法使用 WebAPI 。所幸小喵找到了一個好朋友 Donma 分享的文章,提供了解決的方法~

 

範例物件

這邊簡單用個UserInfo類別來當作範例物件,相關的內容如下:

Public Class UserInfo
    Public Property UserName As String = ""
    Public Property Age As Integer = 0
End Class

 

WebService

接著撰寫 WebService 的部分。首先是因為要傳回JSON的格式,先 Imports System.Web.Script.Service

Imports System.Web.Script.Services

接著,小喵用了兩個練習:

  1. 透過GET來取得多筆的資料,傳回物件集合,轉JSON格式
  2. 透過POST送資料過來,最後傳回物件,轉JSON格式

透過 ScriptMothod的宣告,GetUsers 這個不需要傳入資料的部分,宣告可以使用 GET 來呼叫,而傳回的內容指定為JSON (Client端還是要配合指定傳回格式)

相關的程式碼如下:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Web.Script.Services

' 若要允許使用 ASP.NET AJAX 從指令碼呼叫此 Web 服務,請取消註解下列一行。
<System.Web.Script.Services.ScriptService()> _
<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class WSUser
    Inherits System.Web.Services.WebService

    <WebMethod()> _
    <ScriptMethod(UseHttpGet:=True, ResponseFormat:=ResponseFormat.Json)> _
    Public Function GetUsers() As List(Of UserInfo)
        Try
            Dim y As Integer
            Dim oUsers As New List(Of UserInfo)
            Dim tUser As UserInfo
            
            '迴圈產生傳回的資料
            For y = 1 To 10
                tUser = New UserInfo
                tUser.UserName = "User" & y.ToString("00")
                tUser.Age = 20 + y
                oUsers.Add(tUser)
            Next
            Return oUsers

        Catch ex As Exception
            Throw
        End Try
    End Function

    <WebMethod> _
    <ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
    Public Function GetOneUser(ByVal UserName As String, ByVal Age As Integer) As UserInfo
        Try
            Dim rtnUser As New UserInfo
            rtnUser.UserName = UserName
            rtnUser.Age = Age
            Return rtnUser
        Catch ex As Exception
            Throw
        End Try
    End Function

End Class

ScriptMethod中宣告傳回的格式是Json,不過使用端還是要配合設定,不然傳回的預設還是xml。傳回xml或json在Server端這邊程式不必特別為Client端分開寫,有WebAPI 類似的方便。

因為其中 GetUsers 想透過 GET來呼叫,所以在 Web.Config 中特別設定一下,在System.Web的段落中,加入以下的內容

    <webServices>
      <protocols>
        <add name="HttpSoap"/>
        <add name="HttpPost"/>
        <add name="HttpGet"/>
      </protocols>
    </webServices>

這樣測試一下GetUsers,跑出來如下的內容

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<ArrayOfUserInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
  <UserInfo>
    <UserName>User01</UserName>
    <Age>21</Age>
  </UserInfo>
  <UserInfo>
    <UserName>User02</UserName>
    <Age>22</Age>
  </UserInfo>
  <UserInfo>
    <UserName>User03</UserName>
    <Age>23</Age>
  </UserInfo>
  <UserInfo>
    <UserName>User04</UserName>
    <Age>24</Age>
  </UserInfo>
  <UserInfo>
    <UserName>User05</UserName>
    <Age>25</Age>
  </UserInfo>
  <UserInfo>
    <UserName>User06</UserName>
    <Age>26</Age>
  </UserInfo>
  <UserInfo>
    <UserName>User07</UserName>
    <Age>27</Age>
  </UserInfo>
  <UserInfo>
    <UserName>User08</UserName>
    <Age>28</Age>
  </UserInfo>
  <UserInfo>
    <UserName>User09</UserName>
    <Age>29</Age>
  </UserInfo>
  <UserInfo>
    <UserName>User10</UserName>
    <Age>30</Age>
  </UserInfo>
</ArrayOfUserInfo>

不是指定要丟回JSON嗎,怎麼還是跑出xml ?

其實不必擔心,只要Client端呼叫的時候,指定Content-Type, dataType即可

 

jQuery呼叫範例:

WebService準備好了,接下來就寫呼叫引用的JavaScript,首先準備測試的畫面:

        <input type="button" id="btnGet" value="取得Users" />
        <br />
        UserName:<input type="text" id="txtUserName" value="" /><br />
        Age:<input type="number" id="numAge" value="0" /><br />
        <input type="button" id="btnGetOneUser" value="設定並取得User" />
        <hr />
        <textarea id="ta1" cols="60" rows="20"></textarea>

相關 jQuery Ajax的範例如下:

    <script>
        var strURL1 = 'http://localhost:48314/WSUser.asmx/GetUsers';
        var strURL2 = 'http://localhost:48314/WSUser.asmx/GetOneUser';

        $(document).ready(function () {
            $('#btnGet').click(getUsers);
            $('#btnGetOneUser').click(SetAndGetOneUser);
        });

        function SetAndGetOneUser() {
            var UserName = $('#txtUserName').val();
            var Age = $('#numAge').val();
            $.ajax({
                type: "POST",
                url: strURL2,
                contentType: "application/json; charset=utf-8",
                data:'{UserName:"' + UserName + '",Age:' + Age + '}',
                dataType: "json",
                success: function (data) {
                    if (data.hasOwnProperty("d")) {
                        $('#ta1').text(JSON.stringify(data.d));
                    }
                    else {
                        $('#ta1').text(JSON.stringify(data));
                    }
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    alert(xhr.status);
                    alert(thrownError);
                }
            });
        }

        function getUsers() {
            $.ajax({
                type: "GET",
                url: strURL1,
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (data) {
                    if (data.hasOwnProperty("d")) {
                        $('#ta1').text(JSON.stringify(data.d));
                    }
                    else {
                        $('#ta1').text(JSON.stringify(data));
                    }
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    alert(xhr.status);
                    alert(thrownError);
                }
            });
        }
    </script>

這樣產出的JSON,會有個怪怪的 d 如下:

所以會透過 data.hasOwnProperty 的方式判斷是否有那個 d 如果有,就取 d 以下的內容

 

以上好了後,在同一個專案中,沒有問題,不過跨了專案或者Domain之後,又會發生 jQuery 跨 Domain的問題,之前小喵有一篇【[WebAPI][CORS]使用 xdomain.js 實現 WebAPI 多組(Multiple) 跨 Domain】,有個透過 XDomain 的方式可以處理,因此借用這個方式,就可以輕鬆解決跨網域的問題,繼續提供另一個專案跨網域使用jQuery的 AJAX 來呼叫,相關的設定請參考那篇文章的說明。

 

末記

在環境上的不允許的狀況下,不得以用Web Service來處理,再次感恩好朋友 Donma 大大的文章,非常有幫助,這樣WebService也可以依據Client端的需求,做到傳回物件,就自動依據Client端需求傳回 XML 或 JSON 的格式。小喵這篇大致上與 Donma那篇幾乎相同,小喵改寫為 VB.NET 的方式撰寫,另外配合XDomain的方式來幾決 jQuery 的 Ajax 會有 Cross Domain 被拒絕的問題,相關資訊小喵做個筆記,以利未來有需要時的參考,也提供網路上的朋友們參考。

 

^_^

 

參考資料:

特別感恩當麻大的文章:

http://www.dotblogs.com.tw/junegoat/archive/2012/10/08/c-sharp-dot-net-web-service-by-json-test-with-jquery.aspx

 


以下是簽名:


Microsoft MVP
Visual Studio and Development Technologies
(2005~2019/6)