MVC 透過 JSON.NET 創建 Web API

Restful API 為目前主流的資料傳輸方式之一,其 JSON 資料格式更有許多標準函式庫能直接進行處理。底下透過 MVC 設計方式,例用 JSON.NET 方式創建一個實作 GET 的 Web API。

範例檔案 : 20160512_WebAPIJsonNETTemplate.rar

開發環境


  • Visual Studio 2015 Community
  • ASP.NET 4.5.2

開始一個 VS 專案


由「檔案」 > 「新增」 > 「專案」 > 選擇 Visual C-sharp 「Web」 > 「ASP.NET Web 應用程式」 > 選擇 「Web API」 > 右側「變更驗證」 > 「無驗證」

開啟後,可以直接點擊瀏覽器來執行,此時會出現下列畫面,其網址為 http://localhost:9000

創建一個 Controller


於 Controllers 資料夾加入一個新的 Web API 2 的 controller ,此控制器名稱為「jsonnetController」。 可以選擇「具有讀取/寫入動作的 Web API 2 控制器」。如此一來會將 CRUD 的操作預先產生,如下;

namespace WebAPIJsonNETTemplate.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)
        {
        }
    }
}

連接資料庫並將資料用 JSON.NET 準備


假設資料庫名稱為 employees,要讀取的資料表為 employees,內容如下;

id emp_no birth_date first_name last_name gender hire_date
1 10001 1953-09-02 Geo Fac M 1986-06-26
2 10002 1964-06-02 Bez Sim F 1985-11-21

可以簡單透過下列 sql 指令來取得 1990-01-01 後 2 日成為員工的資料;

select * 
from employees 
where 
  CONVERT(VARCHAR(10),DATEADD(day,2,'1990-01-01'),112) >= CONVERT(VARCHAR(10), hire_date, 112) 
  and 
  CONVERT(VARCHAR(10), hire_date, 112) >= CONVERT(VARCHAR(10),'1990-01-01',112);

因此後續將以 api 路由器設定取得日期 (如 1990-01-01) 與天數 (如 2),並用 JSON.NET 方式實作出 json data。

於 jsonnetController.cs 中加入實作連接資料庫、取得資料並將之透過 JSON.NET 轉成 json string 的函式,如下;

// must using libraries
using Newtonsoft.Json;
using System.Data;
using System.Data.SqlClient;
private String generateJsonString(String hire_date, int getday) {
    // prepare database connection
    string connectionString = "Data Source=.\\SQLEXPRESS;Initial Catalog=employees;Persist Security Info=True;User ID=ExampleUser;Password=ExampleUser";

    // save json String
    String jsonData = "";

    // connect to the database
    using (SqlConnection conn = new SqlConnection(connectionString)) {

        // prepare the sql syntax
        string sqlStr = "select * from employees where CONVERT(VARCHAR(10),DATEADD(day,@dp,@hdate),112) >= CONVERT(VARCHAR(10), hire_date, 112) and CONVERT(VARCHAR(10), hire_date, 112) >= CONVERT(VARCHAR(10),@hdate,112);";

        // prepare sql syntax and bind the sql parameter
        SqlCommand sqlCmd = new SqlCommand(sqlStr, conn);
        sqlCmd.Parameters.AddWithValue("hdate", hire_date);
        sqlCmd.Parameters.AddWithValue("dp", getday);

        // use sql data adapter to query the database
        SqlDataAdapter sda = new SqlDataAdapter(sqlCmd);

        try
        {
            // start connection
            conn.Open();

            // query data and load into a data table
            DataTable dt = new DataTable();
            sda.Fill(dt);

            conn.Close();

            // try to transfer SQL data to the data table by the JSON.NET
            jsonData = JsonConvert.SerializeObject(dt, Newtonsoft.Json.Formatting.Indented);
        }
        catch
        {
            // use a dictionary to save status
            Dictionary<string, string> statusLog = new Dictionary<string, string>();
            statusLog.Add("status", "Fetching SQL data is failure.");
            jsonData = JsonConvert.SerializeObject(statusLog, Newtonsoft.Json.Formatting.Indented);
        }
    }

    return jsonData;
}

透過回傳 HttpResponseMessage 將 JSON.NET 內容回傳給 View


於繼承 ApiController 類別的 jsonnetController 的類別中,加入新的 Read 方法,如下;

// include the library to use the HttpResponseMessage class
using System.Net.Http.Headers;

新增一個回傳資料型態為 HttpResponseMessage 的公開 GET API 函式,並以呼叫函式的方法取得 json string,如下;

// GET: api/jsonnet/1990-01-01/2
public HttpResponseMessage Get(String hireDate, int day)
{
    // asume fetch user-defined rows at the beginning of the data table are fetched
    // use String Content to get json string
    StringContent sc = new StringContent(generateJsonString(hireDate, day));

    // set the http header
    sc.Headers.ContentType = new MediaTypeHeaderValue("application/json");

    // create a new http request to send the data in "plain text" (string)
    HttpResponseMessage resp = new HttpResponseMessage();

    // set the content is from StringContent
    resp.Content = sc;

    // return the http response text
    return resp;
}
註解
1. 需要注意 GET 回傳為 HttpResponseMessage 資料型態,因為 JSON.NET 已經為 http 傳輸格式,並非用 String 回傳,若用 String 回傳,則回傳內容中文字會多一組雙引號,如 "[\r\n {\r\n \"emp_no\": 10082,\r\n
2. 若要將傳輸方式改為下載,可以將 MediaTypeHeaderValue("application/json") 改成 MediaTypeHeaderValue("application/octet-stream")
3. 因為 GET 為重載函式,若是傳入參數的數目與資料類型皆相同,則 server 會不清楚傳入哪一個公開 GET() 函式,此時可以 (1) 修改其中一個 GET() 的傳入參數資料型態或數目、(2) 將另一個 GET() 進行刪除。

設定路由器


於 App_Start 資料夾下 WebApiConfig.cs 進行設定,將此新增的 GET() 函式進行註冊,如下;

// JSON.ENT router
config.Routes.MapHttpRoute(
    name: "jsonnet",
    routeTemplate: "api/{controller}/{hireDate}/{day}"
);
註解
需要注意的事為 routeTemplate 內定義的變數名稱需要與 GET() 函式定義的變數數目、順序與名稱皆相同,如 hireDate 與 day。

設定回傳資料型態


雖然使用 JSON.NET 來產生 json data,但部分瀏覽器仍會以 XML 的格式輸出,因此必須將回傳資料格式強制改成 json,可以於 App_Start 資料夾下 WebApiConfig.cs 進行設定,如下;

// force return as json data type
var appXmlType = GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);

使用此 API


開啟瀏覽器後,依照路由器的設定 「 api/{controller}/{hireDate}/{day} 」,可以於輸入 「 http://localhost:3330/api/jsonnet/1990-01-01/2 」 來取得 json 資料,如下;

[
  {
    "emp_no": 10082,
    "birth_date": "1963-09-09T00:00:00",
    "first_name": "Parviz",
    "last_name": "Lortz",
    "gender": "M",
    "hire_date": "1990-01-03T00:00:00"
  },
  {
    "emp_no": 11454,
    "birth_date": "1961-08-07T00:00:00",
    "first_name": "Yolla",
    "last_name": "Zedlitz",
    "gender": "M",
    "hire_date": "1990-01-02T00:00:00"
  },
  {
    "emp_no": 12645,
    "birth_date": "1955-04-01T00:00:00",
    "first_name": "Kyoichi",
    "last_name": "Decaestecker",
    "gender": "F",
    "hire_date": "1990-01-03T00:00:00"
  }
]

results matching ""

    No results matching ""