設計與使用ASP.NET Web API

by vivid 23. 十月 2013 11:42

.NET Magazine國際中文電子雜誌
者:許薰尹
稿:張智凱
文章編號:N131014102
出刊日期:2013/10/23

ASP.NET Web API 主要的設計目標是建立一個強固的服務,以HTTP為基礎,是一個很容易設計出REST架構的Framework。搭配Visual Studio 2012開發工具來設計、測試、裝載以及部署以HTTP為基礎的服務(HTTP-based services)。

HTTP定義許多動詞(Verbs,有時也稱HTTP Method) 來處理用戶端的請求,每一種動詞有不一樣的行為。應用在ASP.NET Web API的動詞包含:

  • GET:用來取得資源。
  • POST:傳送一個Entity到伺服器,由伺服端來處理後續的動作,ASP.NET Web API則應用在資料的新增。
  • PUT:在請求傳送一個Entity,取代掉目地URI中既有的Entity。ASP.NET Web API則應用在資料的修改。
  • DELETE:刪除請求中指明的Entity。ASP.NET Web API則應用在資料的刪除。

本篇文章介紹如何在Visual Studio 2012設計ASP.NET Web API,以及撰寫用戶端程式碼來存取ASP.NET WEB API。

設計ASP.NET Web API

第一步先建立ASP.NET MVC專案,從Visual Studio 2012開發工具 -「File」-「New」-「Project」,在「New Project」對話盒中選取程式語言,例如本範例選擇「Visual C#」,從「Web」分類中,選取「ASP.NET MVC 4 Web Application」,為專案取一個名稱,然後按下「OK」鍵,請參考下圖所示:

clip_image002

圖 1::ASP.NET MVC 4 Web Application。

建立一個ASP.NET MVC 4應用程式「Web API」範本專案,請參考下圖所示:

clip_image004

圖 2:建立ASP.NET WEB API專案。

路由與Action

建立完ASP.NET WEB API專案之後,在專案的App_Start目錄下,包含一個WebApiConfig.cs檔案,這個檔案中定義了ASP.NET WEB API預設的路由機制,參考程式表列如下:

namespace MyWebAPI {
  public static class WebApiConfig {
    public static void Register( HttpConfiguration config ) {
      config.Routes.MapHttpRoute(
          name: "DefaultApi" ,
          routeTemplate: "api/{controller}/{id}" ,
          defaults: new { id = RouteParameter.Optional }
      );

      // Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
      // To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
      // For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
      //config.EnableQuerySupport();

      // To disable tracing in your application, please comment out or remove the following line of code
      // For more information, refer to: http://www.asp.net/web-api
      config.EnableSystemDiagnosticsTracing( );
    }
  }
}


ASP.NET WEB API路由是利用HttpRouteCollection類別的MapHttpRoute擴充方法來定義。定義時需要描述URI範本(routeTemplate ),以及範本參數的預設值。例如預設有一個名為「DefaultApi」的路由,URI範本為「api/{controller}/{id}」,預設id設定為選擇性的,可以傳遞id參數或省略。

ASP.NET WEB API服務的程式碼設計在一個.NET類別之中(此類別又稱Controller),它是一個繼承自ApiController的類別,當建立ASP.NET Web API範本專案,Controllers目錄中便包含一個ValuesController.cs檔案,定義一個ASP.NET WEB API服務範本程式,ValuesController類別,其中包含Get、Post、Put、Delete等等方法(又稱Action):

namespace MyWebAPI.Controllers {
  public class ValuesController : ApiController {
    // GET api/values
    public IEnumerable<string> Get( ) {
      return new string [ ] { "value1" , "value2" };
    }

    // GET api/values/5
    public string Get( int id ) {
      return "value";
    }

    // POST api/values
    public void Post( [FromBody]string value ) {
    }

    // PUT api/values/5
    public void Put( int id , [FromBody]string value ) {
    }

    // DELETE api/values/5
    public void Delete( int id ) {
    }
  }
}


 

ApiController類別在設計時有一些限制,除了要繼承自ApiController類別之外,類別名稱要以「Controller」結尾。當ASP.NET WEB API接收到用戶端送出的請求,便會檢查請求是否符合路由範本的規則,然後找尋適合的Controller來做後續處理。例如當你執行上步驟建立的專案,在瀏覽器上輸入以下URL:

http://localhost:8716/api/values」,根據routeTemplate的定義「api/{controller}/{id}」,ASP.NET WEB API便找尋ValuesController類別來做後續處理。

用戶端在發出請求的URL上,不需要指明想要執行的方法(Method,又稱Action),而是根據用戶端發出請求時使用的HTTP Method,叫用ASP.NET WEB API 同名稱(或以HTTP Method開頭)的方法。例如在瀏覽器輸入ASP.NET WEB API所在URL「http://localhost:8716/api/values」,瀏覽器會送出HTTP GET請求到ASP.NET WEB API,因此將會叫用ASP.NET WEB API的Get方法。伺服端則根據用戶端發出的請求標頭來決定要送出XML或JSON資料給用戶端,以下是使用Chrome瀏覽器呼叫時,回傳的XML:

clip_image006

圖 3:以HTTP GET呼叫ASP.NET Web API。

若修改Get方法,將其命名為GetData,也可以照常執行,根據預設的命名原則,只要以HTTP動詞開頭(GET)命名方法,就會自動對應到HTTP GET:

public IEnumerable<string> GetData( ) {
   return new string [ ] { "value1" , "value2" };
}

 

若想要改變為其它名稱不以HTTP動詞開始命名,例如想要將上述GetData方法改名為RetriveData,這時只需要在方法上方套用AcceptVerbs attribute:

[AcceptVerbs( "GET" )]
public IEnumerable<string> RetriveData( ) {
  return new string [ ] { "value1" , "value2" };
}

或使用ActionMethodSelectorAttribute ,如HttpGet attribute:

[HttpGet]
public IEnumerable<string> RetriveData( ) {
   return new string [ ] { "value1" , "value2" };
}

 

參數

用戶端若想在發出請求時傳遞參數到伺服端上的ASP.NET WEB API,可以以兩種方式進行:

· Message-URI:在URI上指明參數,通常用來傳遞簡單型別,如bool、int、datetime、string…等。

· Entity-body:參數放在HTTP訊息的body部分,參數可以是複雜型別。

例如在目前的專案執行時,從瀏覽器輸入以下URL:

http://localhost:8716/api/values/0

便會叫用ValuesController,帶一個id參數的Get方法,ASP.NET WEB API會使用parameter bindings功能,將URL上的「0」傳遞到id參數。得到的執行結果如下圖所示:

clip_image008

圖 4:取回一筆資料。

新增模型

現在,讓我們為專案設計模型,以利用Entity Framework進行資料存取。從「Solution Explorer」視窗,選取專案下「Models」目錄,按滑鼠右鍵,選取「Add」-「New Item」加入一個新項目,選取「Class」,使用「Employee」當作名稱,按「Add」按鈕,請參考下圖所示:

clip_image010

圖 5:新增Employee類別。

為Employee類別加入Id、EmployeeName與Age三個屬性:

namespace MyWebAPI.Models {
  public class Employee {
    public int Id { get; set; }
    public string EmployeeName { get; set; }
    public int Age { get; set; }
  }
}

按相同方式,從「Solution Explorer」視窗,選取專案下「Models」目錄,按滑鼠右鍵,選取「Add」-「New Item」加入一個新項目,選取「Class」,使用「EmployeeDB」當作名稱,按「Add」按鈕,請參考下圖所示:

clip_image012

圖 6:新增EmployeeDB類別。

加入以下程式碼,讓EmployeesDB繼承自DbContext類別( 定義在System.Data.Entity命名空間下),以透過Entity Framework Code First技術連接到資料庫。類別中包含一個Employees屬性,存放Entity物件:

namespace MyWebAPI.Models {
  public class EmployeesDB : DbContext {
    public DbSet<Employee> Employees { get; set; }
  }
}

接著再新增一個EmployeesInitializer類別,繼承DropCreateDatabaseAlways<T>類別,改寫Seed方法,此方法用於初始化資料庫資料表的資料:

 

public class EmployeesInitializer : DropCreateDatabaseAlways<EmployeesDB> {
  protected override void Seed( EmployeesDB context ) {
    base.Seed( context );
    var emps = new List<Employee>{
        new Employee {          
         EmployeeName = "Mary",
        Age =30
       },
         new Employee {          
         EmployeeName = "Candy",
        Age =25
       },
         new Employee {          
         EmployeeName = "Lili",
        Age =45
       }
      };

    emps.ForEach( s => context.Employees.Add( s ) );
    context.SaveChanges( );
  }
}

例如範例中,建立三個Employee物件,並將Employee物件新增到DbContext之中,並叫用SaveChanges方法寫回後端資料庫。

最後修改專案根路徑下的Web.Config檔案,預設有一個connectionStrings組態區段:

<connectionStrings>

<add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MyWebAPI-20130924172807;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MyWebAPI-20130924172807.mdf" />

</connectionStrings>

指定name為EmployeesDB,此名稱需和繼承自DbContext類別的EmployeesDB類別名稱一致,這樣Enity Framework就會自動識別,讀取組態檔案中的連接字串連接到資料庫:

<connectionStrings>

<add name="EmployeesDB" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=EmployeesDB;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\EmployeesDB.mdf" />

</connectionStrings>

初始化資料庫

接著,在Global.asax檔案內Application_Start方法中,加入程式碼,叫用Database類別SetInitializer方法來初始資料庫結構和資料表資料:

protected void Application_Start( ) {
      AreaRegistration.RegisterAllAreas( );

      Database.SetInitializer<MyWebAPI.Models.EmployeesDB>( new MyWebAPI.Models.EmployeesInitializer( ) );

      WebApiConfig.Register( GlobalConfiguration.Configuration );
      FilterConfig.RegisterGlobalFilters( GlobalFilters.Filters );
      RouteConfig.RegisterRoutes( RouteTable.Routes );
      BundleConfig.RegisterBundles( BundleTable.Bundles );
    }


設計整合Entity Framework的API Controller

在此步驟,我們要建立一個設計整合Entity Framework的API Controller,從Visual Studio 2012開發工具-「Solution Explorer」- 你的專案-「Controllers」目錄上方按滑鼠右鍵,從快捷選單選擇「Add」- 「Controller」。設定控制器名稱為「EmployeesController」;在這個階段Template設定為「API controller with read/write actions, using Entity Framework」,Model Class選擇「Employee」,Data context class選取「EmployeesDB」,然後按下「Add 」按鈕,請參考下圖所示:

clip_image014

圖 7:新增Controller。

當你按下「Add」按鈕之後,Visual Studio 2012會自動產生以下程式碼,利用Entity Framework,完成所有新增、刪除、修改、查詢資料庫資料的Action Method:

namespace MyWebAPI.Controllers {
  public class EmployeesController : ApiController {
    private EmployeesDB db = new EmployeesDB( );

    // GET api/Employees
    public IEnumerable<Employee> GetEmployees( ) {
      return db.Employees.AsEnumerable( );
    }

    // GET api/Employees/5
    public Employee GetEmployee( int id ) {
      Employee employee = db.Employees.Find( id );
      if ( employee == null ) {
        throw new HttpResponseException( Request.CreateResponse( HttpStatusCode.NotFound ) );
      }

      return employee;
    }

    // PUT api/Employees/5
    public HttpResponseMessage PutEmployee( int id , Employee employee ) {
      if ( !ModelState.IsValid ) {
        return Request.CreateErrorResponse( HttpStatusCode.BadRequest , ModelState );
      }

      if ( id != employee.Id ) {
        return Request.CreateResponse( HttpStatusCode.BadRequest );
      }

      db.Entry( employee ).State = EntityState.Modified;

      try {
        db.SaveChanges( );
      } catch ( DbUpdateConcurrencyException ex ) {
        return Request.CreateErrorResponse( HttpStatusCode.NotFound , ex );
      }

      return Request.CreateResponse( HttpStatusCode.OK );
    }

    // POST api/Employees
    public HttpResponseMessage PostEmployee( Employee employee ) {
      if ( ModelState.IsValid ) {
        db.Employees.Add( employee );
        db.SaveChanges( );

        HttpResponseMessage response = Request.CreateResponse( HttpStatusCode.Created , employee );
        response.Headers.Location = new Uri( Url.Link( "DefaultApi" , new { id = employee.Id } ) );
        return response;
      }
      else {
        return Request.CreateErrorResponse( HttpStatusCode.BadRequest , ModelState );
      }
    }

    // DELETE api/Employees/5
    public HttpResponseMessage DeleteEmployee( int id ) {
      Employee employee = db.Employees.Find( id );
      if ( employee == null ) {
        return Request.CreateResponse( HttpStatusCode.NotFound );
      }

      db.Employees.Remove( employee );

      try {
        db.SaveChanges( );
      } catch ( DbUpdateConcurrencyException ex ) {
        return Request.CreateErrorResponse( HttpStatusCode.NotFound , ex );
      }

      return Request.CreateResponse( HttpStatusCode.OK , employee );
    }

    protected override void Dispose( bool disposing ) {
      db.Dispose( );
      base.Dispose( disposing );
    }
  }
}

HttpRequestMessage與HttpResponseMessage

ASP.NET WEB API使用HttpRequestMessage類別來表示用戶端傳送過來的HTTP請求;HttpResponseMessage類別則可以用來Action Method回傳的結果,例如回傳HTTP 404狀態碼。你可以利用Request.CreateResponse 或Request.CreateResponse<T>方法來建立HttpResponseMessage。

例如PostEmployee方法建立一個新Employee物件後,需設定location header,並回傳HTTP狀態碼201,因此可利用Request.CreateResponse方法來建立回應。

HttpResponseException

ASP.NET WEB API提供HttpResponseException類別,以丟出例外錯誤給用戶端程式攔截。例如範例中的GetEmployee方法,當找不到對應的Employee物件時,利用HttpResponseException類別丟出例外和HTTP狀態碼。

HTML 用戶端

除了在瀏覽器輸入ASP.NET WEB API所在網址,使用HTTP Get呼叫ASP.NET Web API之外,另一種透過瀏覽器來呼叫的方式是使用HTML FORM,例如我們可以在專案之中加入一個HTML網頁,並加入以HTML標籤,讓使用者以HTTP POST方式傳送請求呼叫ASP.NET WEB API以新增資料到資料庫:

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
    <title> </title>
</head>
<body>
    <form name = "newLocation" action = "api/employees/" method = "post">    
    <input type = "text" name = "EmployeeName" /> <br />
    <input type = "text" name = "Age" /> <br />
    <input type = "submit">
</form>
</body>
</html>

當網頁執行時,輸入資料按下「送出查詢」按鈕,就可以送出資料做新增,請參考下圖所示:

clip_image016

圖 8:新增資料。

此時若重新查詢ASP.NET WEB API,資料已被新增到伺服端資料庫了,查詢結果,請參考下圖所示:

clip_image018

圖 9:查詢已新增的資料。

這個網頁產生的POST封包如下:

POST http://localhost:8716/api/employees/ HTTP/1.1

Accept: text/html, application/xhtml+xml, */*

Referer: http://localhost:8716/htmlpage1.html

Accept-Language: zh-Hant-TW,zh-Hant;q=0.8,en-US;q=0.5,en;q=0.3

User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)

Content-Type: application/x-www-form-urlencoded

Accept-Encoding: gzip, deflate

Connection: Keep-Alive

Content-Length: 26

DNT: 1

Host: localhost:8716

Pragma: no-cache

EmployeeName=Apple&Age=34

 

伺服端回應新的location與新建立的資料:

HTTP/1.1 201 Created

Cache-Control: no-cache

Pragma: no-cache

Content-Type: application/json; charset=utf-8

Expires: -1

Location: http://localhost:8716/api/employees/3

Server: Microsoft-IIS/8.0

X-AspNet-Version: 4.0.30319

X-SourceFiles: =?UTF-8?B?QzpcVlZWXG5ldG1hZ1xXZWJBUElcTXlXZWJBUElcTXlXZWJBUElcYXBpXGVtcGxveWVlc1w=?=

X-Powered-By: ASP.NET

Date: Thu, 26 Sep 2013 01:54:16 GMT

Content-Length: 41

{"Id":3,"EmployeeName":"Apple","Age":34}

 

使用HttpClient呼叫ASP.NET WEB API

ASP.NET WEB API提供一組新的用戶端應用程式開發介面,以叫用HTTP服務,相關的命名空間在System.Net.Http.HttpClient命名空間下,透過此命名空間下的HttpClient類別來傳送HTTP請求與回應。HttpClient類別支援Task可以非同步開發模型來叫用服務。

在專案的HomeController類別Index方法中,加入以下程式碼,建立HttpClient物件,BaseAddress指定為ASP.NET WEB API所在網址,然後叫用GetAsync方法,以非同步方式取得服務執行的結果後,從Result屬性取得執行結果:

public ActionResult Index( ) {
  HttpClient client = new HttpClient( );
  client.BaseAddress = new Uri( "http://localhost:8716/" );
  HttpResponseMessage resp = client.GetAsync( "api/employees" ).Result;
  IEnumerable<Employee> data = null;
  if ( resp.IsSuccessStatusCode ) {
    data = resp.Content.ReadAsAsync<IEnumerable<Employee>>( ).Result;
    ViewBag.Result = data.Count<Employee>( );
  }
  return View( data );
}

範例中使用定義在HttpContentExtensions類別中的ReadAsAsync<T>擴充方法來進行還原序列化,HttpContentExtensions類別的定義位於System.Net.Http.Formatting.dll組件之中,可使用Visual Studio 2012 Nuget管理員,下載Microsoft.AspNet.WebApi.Client套件來取得。

最後利用Count取得回傳的資料筆數,利用ViewBag傳到View:

View

Index View則加入程式如下,從ViewBag取得回傳的資料筆數顯示在畫面上,並利用迴圈將Controller傳至的IEnumerable<MyWebAPI.Models.Employee>中每一個Employee物件的EmployeeName屬性讀出,顯示在無序清單之中:

 

@using System.Collections.Generic
@model    IEnumerable<MyWebAPI.Models.Employee>

<div id="body">
HttpClient執行結果:   @ViewBag.Result 筆
<ul>
    @foreach (var emp in Model)
    {
    <li>@emp.EmployeeName</li>    
    }
   
</ul>
</div>

此範例執行的結果如下圖所示:

clip_image020

圖 10:呼叫ASP.NET WEB API。

使用AJAX呼叫ASP.NET WEB API - Get

最後我們來探討利用jQeury與AJAX來呼叫ASP.NET WEB API,在HomeController加入以下GetByScript方法,回傳View:

public ActionResult GetByScript( ) {

return View( );

}

將滑鼠游標停留在HomeController GetByScript方法上方按滑鼠右鍵,然後選取快捷選單上的「Add View」選項,並勾選「Use a layout or master page」,請參考下圖所示:

clip_image022

圖 11:產生View。

在新建立的Views\Home\ GetByScript.cshtml中加入以下jQuery程式碼,以HTTP Get呼叫ASP.NET WEB API:

<div id = "body">
    <div id = "result"> </div>
</div>
@section scripts{
    <script>
        $.ajax({
            url: 'http://localhost:8716/api/employees',
            type: 'GET',
            contentType: "application/json;charset=utf-8",
            success: function (data) {
                var r = "";
                for (var i = 0; i < data.length; i++) {
                    r += "[" +data[i].EmployeeName + "," + data[i].Age +"] " ;
                }
                $("#result").text(r);
            },
            error: function (x) {
                alert("error");
            }
        });
    </script>
}

此範例執行的結果如下圖所示:

clip_image024

圖 12:以HTTP Get呼叫ASP.NET WEB API。

使用AJAX呼叫ASP.NET WEB API – POST

在HomeController加入以下GetByScript方法,回傳View:

public ActionResult PostByScript( ) {

return View( );

}

將滑鼠游標停留在HomeController PostByScript方法上方按滑鼠右鍵,然後選取快捷選單上的「Add View」選項,並勾選「Use a layout or master page」,按下「Add」按鈕,然後在新建立的Views\Home\ PostByScript.cshtml中加入以下jQuery程式碼,以HTTP POST呼叫ASP.NET WEB API,範例中建立一個emp物件,並將此物件序列化之後,指定在data參數,送到伺服端做新增:

@{
    ViewBag.Title = "PostByScript";
}

<h2> PostByScript </h2>

@section scripts{

    <script>
        var emp = {
            EmployeeName: 'Jasper',
            Age : 37
        };

        $.ajax({
            url: 'http://localhost:8716/api/employees',
            type: 'POST',
            data: JSON.stringify(emp),
            contentType: "application/json;charset=utf-8",
            success: function (data) {
                alert("success");
            },
            error: function (x) {
                alert("error");
            }
        });
    </script>
}


此範例執行的結果如下圖所示:

clip_image026

圖 13:以HTTP POST呼叫ASP.NET WEB API

此時若重新查詢資料庫資料便可看到新增的資料:

clip_image028

圖 14:資料已新增到資料庫。

使用AJAX呼叫ASP.NET WEB API - PUT

在HomeController加入以下PutByScript方法,回傳View:

public ActionResult PutByScript( ) {

return View( );

}

將滑鼠游標停留在HomeController PutByScript方法上方按滑鼠右鍵,然後選取快捷選單上的「Add View」選項,並勾選「Use a layout or master page」,按下「Add」按鈕,然後在新建立的Views\Home\ PutByScript.cshtml中加入以下jQuery程式碼,以HTTP PUT呼叫ASP.NET WEB API,範例中建立一個emp物件,指明要修改id為1的Employee資料,並將此物件序列化之後,指定在data參數,送到伺服端做新增:

@{
    ViewBag.Title = "PostByScript";
}

<h2> PutByScript </h2>

@section scripts{

      <script>
          //update
          var id =1
          var emp = {
              Id:id,
              EmployeeName: ' new Mary',
              Age: 50
          };

          $.ajax({
              url: 'http://localhost:8716/api/employees/' + id,
              cache: false,
              type: 'PUT',
              contentType: 'application/json; charset=utf-8',
              data: JSON.stringify(emp),
              success: function () {
                  alert('update');
              }
          });

        </script>

}


使用AJAX呼叫ASP.NET WEB API - DELETE

在HomeController加入以下DeleteByScript方法,回傳View:

public ActionResult DeleteByScript( ) {

return View( );

}

將滑鼠游標停留在HomeController DeleteByScript方法上方按滑鼠右鍵,然後選取快捷選單上的「Add View」選項,並勾選「Use a layout or master page」,按下「Add」按鈕,然後在新建立的Views\Home\ DeleteByScript.cshtml中加入以下jQuery程式碼,以HTTP Delete呼叫ASP.NET WEB API,刪除id為1的Employee資料:

@{
    ViewBag.Title = "PostByScript";
}
<h2> PutByScript </h2>

@section scripts{

      <script>
          var id = 1
          $.ajax({ type: "DELETE", url: 'http://localhost:8716/api/employees/' +id })
           .done(function () {
               alert('delete');
           });
        </script>
}

Tags:

.NET Magazine國際中文電子雜誌 | ASP.NET Web API | jQuery | 許薰尹Vivid Hsu

評論 (62) -

Dio
Dio Taiwan
2015/3/3 下午 03:23:49 #

受益良多,Thanks !!

回覆

cours de theatre
cours de theatre United States
2017/9/30 上午 06:32:11 #

Im grateful for the article.Really looking forward to read more. Keep writing.

回覆

can ho dic phoenix
can ho dic phoenix United States
2017/10/6 下午 11:59:42 #

Thanks for sharing, this is a fantastic blog post.Really looking forward to read more. Want more.

回覆

Buy illegal backlinks
Buy illegal backlinks United States
2017/10/9 下午 01:54:31 #

Really appreciate you sharing this article.Much thanks again. Fantastic.

回覆

sklep z lekami na potencje
sklep z lekami na potencje United States
2017/10/9 下午 03:31:34 #

Major thankies for the post.Thanks Again. Keep writing.

回覆

can ho bien vung tau
can ho bien vung tau United States
2017/10/9 下午 05:48:15 #

Really enjoyed this blog post. Really Cool.

回覆

solarmovie
solarmovie United States
2017/10/10 下午 07:36:31 #

Really enjoyed this article. Really Great.

回覆

Nathan Coombe
Nathan Coombe United States
2017/10/10 下午 09:43:23 #

Looking forward to reading more. Great article.

回覆

pirater un compte facebook
pirater un compte facebook United States
2017/10/11 上午 12:01:59 #

I really enjoy the post.Much thanks again. Much obliged.

回覆

buy hacklink google
buy hacklink google United States
2017/10/12 下午 08:26:57 #

Thanks for the article.Much thanks again.

回覆

you could check here
you could check here United States
2017/10/14 下午 03:42:28 #

Hey, thanks for the blog post. Keep writing.

回覆

dragon city hack unlimited gems apk
dragon city hack unlimited gems apk United States
2017/10/15 下午 03:10:44 #

Really informative article post.Really thank you! Cool.

回覆

omega xl review
omega xl review United States
2017/10/15 下午 07:52:34 #

A round of applause for your post. Really Cool.

回覆

Read Full Article
Read Full Article United States
2017/10/17 下午 02:29:28 #

I think this is a real great blog post.Much thanks again. Will read on...

回覆

sletrokor
sletrokor United States
2017/10/17 下午 08:00:53 #

Thanks again for the blog.Really looking forward to read more. Great.

回覆

Osimi Tower
Osimi Tower United States
2017/10/19 上午 05:23:16 #

Really enjoyed this blog.Thanks Again. Keep writing.

回覆

find out here
find out here United States
2017/10/19 下午 05:57:06 #

Say, you got a nice blog post.Much thanks again. Great.

回覆

pure slim 1000 review
pure slim 1000 review United States
2017/10/20 上午 03:28:09 #

A round of applause for your post.Really thank you! Cool.

回覆

can ho son thinh
can ho son thinh United States
2017/10/21 上午 03:11:36 #

Im obliged for the post.Really looking forward to read more. Great.

回覆

elakekassa
elakekassa United States
2017/10/21 下午 05:13:51 #

Thanks again for the blog post.Really thank you! Will read on...

回覆

website designing company in Delhi India
website designing company in Delhi India United States
2017/10/24 上午 11:37:42 #

Really appreciate you sharing this blog. Really Great.

回覆

Osimi Sea View
Osimi Sea View United States
2017/10/28 上午 10:29:11 #

Thank you ever so for you post. Much obliged.

回覆

lepszy plock
lepszy plock United States
2017/10/30 下午 05:07:58 #

Muchos Gracias for your blog article.Really thank you! Really Great.

回覆

life leadership
life leadership United States
2017/11/1 上午 09:37:13 #

I think this is a real great blog article.Much thanks again. Really Cool.

回覆

phenocal
phenocal United States
2017/11/1 下午 05:04:04 #

Awesome blog post.Thanks Again. Great.

回覆

phentaslim review
phentaslim review United States
2017/11/3 下午 12:17:21 #

Say, you got a nice article post.Really thank you! Keep writing.

回覆

spinal stenosis treatment at home
spinal stenosis treatment at home United States
2017/11/15 上午 08:20:22 #

Im grateful for the blog.Really thank you! Much obliged.

回覆

criminel avocat
criminel avocat United States
2017/11/16 下午 06:55:29 #

Enjoyed every bit of your blog post.Thanks Again. Really Great.

回覆

Thank you for your blog.Really looking forward to read more. Awesome.

回覆

swimwear
swimwear United States
2017/11/23 下午 10:24:50 #

Thanks again for the blog.Really thank you! Really Great.

回覆

Chad Boonswang and Jeffrey Goodman
Chad Boonswang and Jeffrey Goodman United States
2017/11/26 下午 06:31:25 #

Really enjoyed this post. Really Great.

回覆

Chad Boonswang SEO
Chad Boonswang SEO United States
2017/11/27 上午 12:37:49 #

I think this is a real great post. Much obliged.

回覆

car wrecker scammer
car wrecker scammer United States
2017/11/29 下午 05:00:19 #

Say, you got a nice article.Thanks Again. Will read on...

回覆

Saigon Pentaview
Saigon Pentaview United States
2017/11/29 下午 11:37:37 #

Major thankies for the blog. Fantastic.

回覆

porno
porno United States
2017/12/1 下午 05:06:59 #

Really enjoyed this blog article.Thanks Again. Want more.

回覆

Get Business Credit
Get Business Credit United States
2017/12/3 上午 05:23:18 #

Major thanks for the article post. Much obliged.

回覆

mika tan
mika tan United States
2017/12/5 上午 10:10:38 #

Im grateful for the blog.Thanks Again.

回覆

Major thankies for the blog article. Great.

回覆

A round of applause for your post.Really looking forward to read more. Really Great.

回覆

law blog
law blog United States
2017/12/10 下午 07:47:05 #

Fantastic post. Much obliged.

回覆

Lewis Klemisch
Lewis Klemisch United States
2017/12/14 上午 09:54:11 #

Thank you ever so for you blog article.Thanks Again. Want more.

回覆

click this site
click this site United States
2017/12/14 下午 04:46:28 #

I am so grateful for your blog post.Really looking forward to read more.

回覆

Christmas Music
Christmas Music United States
2017/12/14 下午 11:19:00 #

Really informative article post.Really looking forward to read more. Will read on...

回覆

canon drivers
canon drivers United States
2017/12/16 下午 06:27:45 #

Im thankful for the blog.Thanks Again. Want more.

回覆

green coffee bean
green coffee bean United States
2017/12/17 上午 12:46:10 #

A big thank you for your blog.Really looking forward to read more. Awesome.

回覆

Major thankies for the post. Want more.

回覆

buying diploma
buying diploma United States
2017/12/18 上午 01:12:19 #

I loved your blog article.Really thank you! Cool.

回覆

financial freedom
financial freedom United States
2017/12/20 下午 07:22:07 #

Im thankful for the blog post.Thanks Again. Keep writing.

回覆

driver canon
driver canon United States
2017/12/23 上午 09:23:37 #

Really enjoyed this blog article.Much thanks again. Fantastic.

回覆

Darwin Horan
Darwin Horan United States
2017/12/23 下午 07:52:18 #

Thank you ever so for you article post. Keep writing.

回覆

I really liked your blog post.Much thanks again. Want more.

回覆

hp driver
hp driver United States
2017/12/25 下午 09:21:17 #

I am so grateful for your blog. Cool.

回覆

&#216;&#177;&#218;&#175; &#216;&#174;&#217;ˆ&#216;&#167;&#216;&#168;
رگ خواب United States
2017/12/26 上午 04:08:35 #

I cannot thank you enough for the blog article.Really thank you! Really Great.

回覆

hp drivers
hp drivers United States
2018/1/2 上午 06:42:24 #

Thanks a lot for the blog article. Great.

回覆

special info
special info United States
2018/1/2 下午 02:40:05 #

I loved your article.Really thank you!

回覆

I really liked your article.Thanks Again. Keep writing.

回覆

hp drivers
hp drivers United States
2018/1/3 上午 05:59:59 #

Very informative blog.Much thanks again. Awesome.

回覆

casino bonus code
casino bonus code United States
2018/1/4 下午 04:33:05 #

Awesome blog post.Really looking forward to read more. Really Cool.

回覆

hp driver
hp driver United States
2018/1/5 下午 04:50:13 #

Muchos Gracias for your blog.Really thank you! Much obliged.

回覆

FBA
FBA United States
2018/1/6 上午 08:55:10 #

Really appreciate you sharing this blog article.Really thank you! Want more.

回覆

web hosting
web hosting United States
2018/1/10 上午 07:41:10 #

I cannot thank you enough for the blog. Keep writing.

回覆

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List