iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 19
1
Modern Web

WebGIS入門學習 - 以Openlayers實作系列 第 19

Day 19. 建立Web API與Postman測試、產生說明文件

前言

今天我們要來學如何建立Web API,並利用Postman進行API的測試,最後安裝Swagger套件來產生API的說明文件。
與WebService相比,API可以在header帶token進行驗證、並鎖CORS、限制IP或domain,較WebService有較多的防護。

不過對於資安的議題除了在前端可以做第一層防護限制格式的輸入上傳檔案重新命名存檔驗證從API接進來的資料格式,在程式撰寫部分最基本的就是不要造成 SQL Injection,並做第二次的輸入格式驗證;軟硬體要做好 內外網分離 將網站放在DMZ區、系統更新弱點掃描異地備份災難復原演練 等方式,資安議題一定會一直存在,只能把該做的事情做好,對於最差的情況就是能快速復原系統 (不考慮資料洩漏的部分)。

講太多廢話了,我們回來開始學如何建立Web API!

今天的大綱

  1. 建立Web API
  2. 使用Postman進行測試並修正介接
  3. 套用Swagger產生說明文件

1. 建立Web API

Web API不是像我們網頁使用的Webfrom模式,而是類似MVC架構(Model-View–Controller),只是沒有View而已。

參考維基百科,MVC架構說明如下:

  • 模型(Model) - 程式設計師編寫程式應有的功能(實現演算法等等)、資料庫專家進行資料管理和資料庫設計(可以實現具體的功能)。
  • 視圖(View) - 介面設計人員進行圖形介面設計。
  • 控制器(Controller)- 負責轉發請求,對請求進行處理。

參考微軟官方文件:ASP.NET Identity 簡介

STEP 1. 首先,先建立一個ASP.NET Web應用程式
https://ithelp.ithome.com.tw/upload/images/20200927/20108631Xo9wBCjGYU.png

STEP 2. 設定專案名稱OLMapAPI和路徑,選擇.NET版本
https://ithelp.ithome.com.tw/upload/images/20200927/20108631nZ0wenNpPK.png

STEP 3. 選擇建立Web API,並於右上角的 驗證 選擇 個別使用者帳戶 驗證,這種驗證方法可以藉由Register 來進行個別帳號的註冊,並透過 ASP.NET Identity 將帳號與加密後的密碼儲存在SQL Server資料表當中。
https://ithelp.ithome.com.tw/upload/images/20200927/20108631wN0r2LfNFy.png

STEP 4. 更改Web.config內的資料庫連線字串,這部分我們在 Day 11 建立WebService時有學過。

<add name="DefaultConnection" connectionString="Data Source=localhost\SQLEXPRESS;Initial Catalog=OLDemo; user id=資料庫帳號;password=資料庫密碼" providerName="System.Data.SqlClient" />

https://ithelp.ithome.com.tw/upload/images/20200927/20108631DOhmlSeAG8.png

這邊可以將 name="DefaultConnection" 改掉,但由於我們使用 Identity.EntityFramework 來進行帳號的儲存,因此若要將Default改掉,則在 IdentityModels.cs 這個頁面 的base("DefaultConnection", throwIfV1Schema: false) 字串也要改掉。 (本系列講解API的部分暫時維持 "DefaultConnection")

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }   
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}

STEP 5. 在Models下新增 BasicModels.cs 程式碼頁面

https://ithelp.ithome.com.tw/upload/images/20200927/20108631G7uItMlitH.png

STEP 6.BasicModels.cs 內新增 LayerResourceList 的類別,為圖層資料的輸出類別。

public class LayerResourceList
    {
        public string ID { get; set; }
        public string GroupID { get; set; }
        public string GroupName { get; set; }
        public string LayerID { get; set; }
        public string LayerOrder { get; set; }
        public string LayerQueryable { get; set; }
        public string LayerTitle { get; set; }
        public string LayerType { get; set; }
        public string DataType { get; set; }
        public string DataURL { get; set; }
        public string LayerVisibleCode { get; set; }
        public string OpenOpacity { get; set; }
    }

STEP 5. (新增) Infrastructure\BasicInfo 資料夾底下新增BasicInfoFunc.cs,並將從資料庫撈取資料的程式碼 getLayerResourceList() 寫在這支裡面:

using System;
using System.Collections.Generic;
using OLMapAPI.Models;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
using OLMapAPI.Models.auth;
using System.Threading.Tasks;

namespace OLMapAPI.Infrastructure.BasicInfo
{
    public class BasicInfoFunc
    {
        public static async Task<List<LayerResourceList>> getLayerResourceList()
        {
            SqlDataReader dr = null;
            SqlConnection myConnection = new SqlConnection();
            string Constr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
            myConnection.ConnectionString = Constr;
            SqlCommand sqlCmd = new SqlCommand();
            string sqlStr;
            sqlStr = "SELECT [ID] ,[GroupID] ,[GroupName] ,[LayerID] ,[LayerOrder] ,[LayerQueryable] ,[LayerTitle] ,[LayerType],[DataType] ,[DataURL] ,[LayerVisibleCode] ,[OpenOpacity]  FROM [OLDemo].[dbo].[LayerResource]  order by [GroupID], [LayerOrder], [LayerType]";
            sqlCmd.CommandText = sqlStr;
            sqlCmd.CommandType = CommandType.Text;
            sqlCmd.Connection = myConnection;
            List<LayerResourceList> arrList = new List<LayerResourceList>();
            try
            {
                myConnection.Open();
                dr = sqlCmd.ExecuteReader();

                while (dr.Read())
                {
                    arrList.Add(new LayerResourceList()
                    {
                        ID = dr["ID"].ToString(),
                        GroupID = dr["GroupID"].ToString(),
                        GroupName = dr["GroupName"].ToString(),
                        LayerID = dr["LayerID"].ToString(),
                        LayerOrder = dr["LayerOrder"].ToString(),
                        LayerQueryable = dr["LayerQueryable"].ToString(),
                        LayerTitle = dr["LayerTitle"].ToString(),
                        LayerType = dr["LayerType"].ToString(),
                        DataType = dr["DataType"].ToString(),
                        DataURL = dr["DataURL"].ToString(),
                        LayerVisibleCode = dr["LayerVisibleCode"].ToString(),
                        OpenOpacity = dr["OpenOpacity"].ToString()
                    });
                }
                myConnection.Close();
                myConnection.Dispose();
                return arrList;
            }
            catch (Exception ex)
            {
                throw;
            }
        }
    };
}

STEP 6.Controllers資料夾底下新增LayersController.cs,具有讀取/寫入動作的MVC 5 控制器,也可以新增空白的自己刻。
https://ithelp.ithome.com.tw/upload/images/20200927/20108631FhYVzY9uwf.png

https://ithelp.ithome.com.tw/upload/images/20200927/20108631Hbj23Yaglg.png

STEP 7. 在Controller內新增 getLayerResource() 功能

using OLMapAPI.Infrastructure.BasicInfo;
using OLMapAPI.Models;
using Swashbuckle.Swagger.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;

namespace OLMapAPI.Controllers
{
    //[Authorize]
    [RoutePrefix("api/Layers")]
    public class LayersController : ApiController
    {
        [Route("getLayerResource")]
        [HttpGet]
        public async Task<HttpResponseMessage> getLayerResource()
        {
            try
            {
                return Request.CreateResponse(HttpStatusCode.OK, await BasicInfoFunc.getLayerResourceList());
            }
            catch (Exception SqlException)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, "Internal Server Error");
            }
        }
    }
}

將api架在IIS上:http://localhost/OLMapAPI

2. 使用Postman進行測試並修正介接

STEP 1. 安裝 postman

STEP 2. 輸入API資料:

GET - http://localhost/OLMapAPI/api/Layers/getLayerResource

https://ithelp.ithome.com.tw/upload/images/20200927/20108631UdssSZ8WSl.png

輸出 (截部分)

[
    {
        "ID": "1",
        "GroupID": "nlsc",
        "GroupName": "國土測繪中心",
        "LayerID": "nlsc_school",
        "LayerOrder": "1",
        "LayerQueryable": "False",
        "LayerTitle": "各級學校範圍圖",
        "LayerType": "Tile",
        "DataType": "WMS",
        "DataURL": "https://wms.nlsc.gov.tw/wms",
        "LayerVisibleCode": "SCHOOL",
        "OpenOpacity": "0"
    },
    {
        "ID": "5",
        "GroupID": "opendata",
        "GroupName": "OpenData",
        "LayerID": "DebrisArea_geojson",
        "LayerOrder": "1",
        "LayerQueryable": "False",
        "LayerTitle": "土石流潛勢溪流影響範圍圖",
        "LayerType": "Vector",
        "DataType": "GeoJSON",
        "DataURL": "data/GeoJSON",
        "LayerVisibleCode": "DebrisArea.Geojson",
        "OpenOpacity": "0"
    }
]

STEP 3. 圖台部分於 init.js 新增Api路徑

var config_OLMapWebAPI = "http://localhost/OLMapAPI/api";

STEP 4. 修正 jTOC.js 的介接ajax內容,修正後:

//修正後
$.ajax({
    //type: "POST",
    //url: config_WSLayerResource + "/getLayerResource",
    type: "GET",
    url: config_OLMapWebAPI + "/Layers/getLayerResource",
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    success: function (d) {
        //var data = $.parseJSON(d.d);
        var data = d;
        console.log(data);
        var layerlisthtml = "";
        TOCArray = [];
        $.each(data, function (index, item) {
            loadLayer(item);
            if (typehasExtent.includes(item.DataType)) {
                layerlisthtml += '<div class="item"><div class="ui checkbox"><input type="checkbox" name="example" onclick="toc.toggleTocLayer(\'' + item.LayerID + '\', this)"><label></label></div><div class="content"><a class="header">' + item.LayerTitle + '</a><div class="description">' + item.DataType + '</div><img class="layerBtns info" src="images/TOCpage/info.png" title="點擊定位圖層" onclick="toc.zoomTocLayer(\'' + item.LayerID + '\')"></div></div>';
            } else {
                layerlisthtml += '<div class="item"><div class="ui checkbox"><input type="checkbox" name="example" onclick="toc.toggleTocLayer(\'' + item.LayerID + '\', this)"><label></label></div><div class="content"><a class="header">' + item.LayerTitle + '</a><div class="description">' + item.DataType + '</div></div></div>';
            }
            TOCArray.push(item);
        });
        $("#layerlist").html(layerlisthtml);
    },
    error: function (jqXHR, exception) {
        ajaxError(jqXHR, exception);
    }
});

STEP 5. 測試網站執行
https://ithelp.ithome.com.tw/upload/images/20200927/20108631yPaTde4DQO.png

測試成功!!

3. 套用Swagger產生說明文件

STEP 1. 使用 工具> NuGet套件管理員> 管理方案的NuGet套件下 安裝 Swashbuckle 套件。
https://ithelp.ithome.com.tw/upload/images/20200927/20108631yhanw1dNzW.png

STEP 2. 進行專案 XML 註解設定,會在 App_Data 資料夾下產生XML文件
https://ithelp.ithome.com.tw/upload/images/20200927/201086318N2sH9n875.png

STEP 3. 在 Controller 方法加上註解,按下 "///" 會自動產生 Comment

/// <summary>
/// 查詢所有圖層資料
/// </summary>
/// <returns></returns>
[Route("getLayerResource")]
[HttpGet]
[SwaggerResponse(HttpStatusCode.OK, "OK", typeof(List<LayerResourceList>))]
public async Task<HttpResponseMessage> getLayerResource()
{
    try
    {
        return Request.CreateResponse(HttpStatusCode.OK, await BasicInfoFunc.getLayerResourceList());
    }
    catch (Exception SqlException)
    {
        return Request.CreateResponse(HttpStatusCode.InternalServerError, "Internal Server Error");
    }
}

STEP 4. 設定 SwaggerConfig.cs 搜尋 IncludeXmlComments > 將此行反註解
https://ithelp.ithome.com.tw/upload/images/20200927/201086316UDLtLordN.png

STEP 5. 註解拿掉後會發現缺少 GetXmlCommentsPath 方法,也就是專案檔設定的 XML 路徑
直接在底下新增下面幾行:

private static string GetXmlCommentsPath() {
    return String.Format(
         @ "{0}\App_Data\XmlDocument.xml",
        AppDomain.CurrentDomain.BaseDirectory);
}

STEP 6. 將專案起始頁指定為swagger
https://ithelp.ithome.com.tw/upload/images/20200927/20108631jIyYt3ioBh.png

STEP 7. 建置後IIS架站,看到swagger頁面就完成拉!
https://ithelp.ithome.com.tw/upload/images/20200927/2010863132WKIEmMOM.png

參考文章:https://marcus116.blogspot.com/2019/01/how-to-add-api-document-using-swagger-in-webapi.html


小結

如同標題所說,我們今天學會了怎麼建Web API、學會了怎麼用Postman測試,最後學會了怎麼套用Swagger產生api說明文件,也就是說一個簡單的API就這樣完成拉!!!

但身為一個API只有這些功能還不夠,只少要能夠進行帳號驗證、權限控管,讓有權限的人才可以使用這個圖台,並在每次撈取檔案的時候就再進行一次身分確認,增加系統的安全性,所以後面的幾天我們就要來講 Web API的權限控管機制的建立 ,可能會稍微複雜,要有心理準備!


上一篇
Day 18. 今天不寫程式改來學知識 #4:好用的OpenData與WebGIS網站
下一篇
Day 20. API登入權限控管機制 #1:ASP.NET Identity
系列文
WebGIS入門學習 - 以Openlayers實作30

尚未有邦友留言

立即登入留言