2018年6月21日 星期四

Swashbuckle (3) 顯示 webapi 方法上 xml 的摘要

設定在 swagger 頁面顯示的 xml 摘要資訊

環境:vs2017、.net framework 4.7、Swashbuckle 5.6.0

首先建立好一個 WebApi 專案和透過 Nuget 裝好 Swashbuckle,並且透過
http://localhost:xxxxx/swagger 能夠進到 Swashbuckle 的頁面。

本篇的目的在於讓 Api 上面的 xml 註解中的資訊出現在 swagger 的頁面上

在安裝好 swashbuckle 後開啟 App_Start 中的 SwaggerConfig.cs,這個檔案
應該在 Nuget 安裝好 Swashbuckle 時就自動產生。

取消 SwaggerConfig.cs 中的下面這一行的註解

    c.IncludeXmlComments(GetXmlCommentsPath());

在同一個 Class ( public class SwaggerConfig ) 下增加方法

   
 internal static string GetXmlCommentsPath()
 {
   //透過設定專案的屬性,設定在建置後生成 XmlDocument.xml,
   //此處是設定 Swagger 參考這份 xml
   return string.Format(@"{0}\App_Data\XmlDocument.xml",
   System.AppDomain.CurrentDomain.BaseDirectory); 
 }

圖上刪了很多原本是註解掉的部份



設定專案屬性,右鍵點專案 > 專案屬性 > 建置頁籤 > 輸出

有一個 XML 文件檔案,勾選並設定檔案的路徑,這個路徑需要和前一步設定
的 XML 路徑對應。


WebApi 的例子
  
/// <summary>
/// 人員
/// </summary>
public class Person
{
    /// <summary>
    /// 人員名稱
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// 人員年紀
    /// </summary>
    public int Age { get; set; }
}

/// <summary>
/// 錯誤狀態
/// </summary>
public class ErrorStatus
{
    /// <summary>
    /// 狀態代碼
    /// </summary>
    public string StatusCode { get; set; }
    /// <summary>
    /// 狀態描述
    /// </summary>
    public string Description { get; set; }
}

 
public class PeopleController : ApiController
{
   /// <summary>
   /// 取得人員資料
   /// </summary>
   /// <remarks>
   /// 取得人員資料 api 的補充說明
   /// </remarks>
   /// <param name="id">人員 Id</param>
   /// <response code="200">成功</response>
   /// <response code="404">找不到</response>
   /// <response code="500">內部伺服器錯誤</response>
   [ResponseType(typeof(Person))]
   public IHttpActionResult Get(string id)
   {
      return Ok(new Person());
   }
}

如果設定了 [ResponseType(typeof(Person))] 因為他預設是在 Http Status 200
的時候所回傳的類別,所以需要同時設定 <response code="200">成功</response>
才會正常顯示 [ResponseType(typeof(Person))] 的部份

在傳入值或回傳值,切換到 Model 下時會顯示定義在 DataModel 上的 <summary> 訊息

執行專案,然後接下來進到 Swagger 頁面就可以看到 webapi 上設定的 xml 註解的資訊


在比較新版的 Swashbuckle 中,可以利用 SwaggerResponse 這個 attribute 來取代
<response code=""></response>

 
public class PeopleController : ApiController
{
  /// <summary>
  /// 取得人員資料
  /// </summary>
  /// <remarks>
  /// 取得人員資料 api 的補充說明
  /// </remarks>
  /// <param name="id">人員 Id</param>
  [SwaggerResponse(HttpStatusCode.OK, Description = "成功", Type = typeof(Person))]
  [SwaggerResponse(HttpStatusCode.NotFound, Description = "找不到")]
  [SwaggerResponse(HttpStatusCode.InternalServerError, Description = "內部伺服器錯誤", Type = typeof(ErrorStatus))]
  [ResponseType(typeof(Person))]
  public IHttpActionResult Get(string id)
  {
    return Ok(new Person());
  }
}


2018年6月20日 星期三

Swashbuckle (1) 基本使用

環境:vs2017、.net framework 4.7、Swashbuckle 5.6.0

Swashbuckle,其實就是 Swagger,基本上是一套產生線上 API
文件的產生器,線上文件(網頁)同時也可以產生一個呼叫 API 的頁面。

在實際使用上有很多不同的進階選項可以使用,本篇只說明最基礎的部份。

建立一個 WebApi 專案,在專案中建立 PeopleController

    PeopleController.cs

public class PeopleController : ApiController
{
    /// <summary>
    /// 取得人員資料
    /// </summary>
    /// <returns></returns>
    public string Get(string id)
    {
        return ("Ricky");
    }
    /// <summary>
    /// 新增人員資料
    /// </summary>
    /// <returns></returns>
    public HttpResponseMessage Post(Person person)
    {
        return Request.CreateResponse(
            HttpStatusCode.Created, new { Status = "OK" });
    }
}


public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

透過 Nuget 安裝套件 Swashbuckle 和 Swashbuckle.Core Swashbuckle 相依於 Swashbuckle.Core 所以實際上裝 Swashbuckle 另 一個會自已裝好。
安裝好後直接執行專案

然後網址輸入 http://localhost:xxxxx/swagger,就能開啟 WebApi 文件的介面



輸入好參數(如果該 api 需要),點下面的 try out 就能呼叫 api

以上就是最基本的使用部份,Swashbuckle 依照 Api Controlle 中的基本資訊
(ex. 參數、回傳值)等,生成頁面上的資訊,但諸如方法上的 <summary> 註解
等都不會反應到頁面上。

Swashbuckle (2) 利用設定 ResponseType 告知 swagger 實際上回傳的型別為何

當 api 回傳的型別是 IHttpActionResult 時,利用設定 ResponseType
告知 swagger 實際上回傳的型別為何。

環境:vs2017、.net framework 4.7、Swashbuckle 5.6.0

透過 ResponseType 屬性,控制 Swagger 頁面所顯示的資訊

WebApi 的內容

//ResponseType 的 namespace 是 System.Web.Http.Description

using System.Web.Http.Description;

public class ValuesController : ApiController {
  [ResponseType(typeof(Person))]
  public IHttpActionResult Get(string id)
  {
    return Ok(new Person());
  }
}

加上 [ResponseType(typeof(Person))] 前後的比對,可以透過 Model 和 Example Value 的切換
切換樣本和回傳的 Model 的定義。



2018年6月12日 星期二

[Visual Studio] 同時輸出到不同的目標 Framework

建立一個 .NET Standard 類型的專案


在專案上點右鍵選「編輯 xxx .csproj」,就是編輯那個專案檔,因為是 .NET Standard 類型
的專案,所以不用先缷載專案。


將 .csproj 檔中的

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

修改成如下,要注意的是 「TargetFramework」修改後「TargetFrameworks」
和裡面的「net45;netstandard1.1」,指的是要產生.net framework 4.5 及
.net standard 1.1 為目標編譯出的類別庫

<PropertyGroup>
<TargetFrameworks>net45;netstandard1.1</TargetFrameworks>
</PropertyGroup>

接下來存檔之後應該會跳訊息要重新載入專案,重新載入即可,在載入完成後可
以在專案的相依性上看到 .NETFramework 4.5 和 .NETStandard 1.1 兩個項目


之後建置專案之後,就可以在專案的 Debug 或 Release 資料夾下看到 net45 和
netstandard1.1 兩個資料夾,裡面就是各自編譯出來的類別庫。



2018年6月9日 星期六

[jQuery] validate.unobtrusive 驗證(3) 自訂驗證

jquery.validate、jquery.validate.unobtrusive 自訂驗證

基本

    $.validator.addMethod 和 $.validator.unobtrusive.adapters.add
    需要在載入 DOM 之前就先執行

下面的例子包括

    不額外傳入驗證條件
    傳入一個驗證條件
    傳入多個驗證條件

不額外傳入驗證條件(ex.自定用來比對的上限/下限值)的例子,本例中假設
欄位字元長度不能超過5

validator.addMethod() 裡的 stringlengthcheck 對應到 html tag 裡的 data-val-stringlengthcheck
<head>
    <script>
        //stringlengthcheck 是在 html tag 中對應的 data-val-stringlengthcheck
        $.validator.addMethod('stringlengthcheck', function (value, element, params) {
            //假設驗證欄位不可大於5
            return value.length <= 5;
        });
        //因為沒有要多傳參數做判斷,所以在 adapters.add 中沒有特別處理
        //為了要做 unobtrusive 的設定
        $.validator.unobtrusive.adapters.add("stringlengthcheck", function (options) {
            options.rules["stringlengthcheck"] = {
            };
            options.messages["stringlengthcheck"] = options.message;
        });
    </script>        
</head>
<body>
    <input type="text" value="" name="money" id="money" data-val="true" data-val-stringlengthcheck="字元長度限制" />
    <span data-valmsg-for="money" data-valmsg-replace="true"></span>            
</body>
傳入一個驗證條件
<head>
    <script>
        //傳入一個額外驗證條件
        //data-val-stringlengthcheck="錯誤訊息" 會觸發驗證
        //$.validator.addMethod 的 stringlengthcheck 要對應到 html tag 的 data-val-stringlengthcheck
        $.validator.addMethod('stringlengthcheck', function (value, element, params)
        {
            //驗證條件,欄位內容長度需要小於
            return value.length <= params["myMaxValue"];
        });
        //$.validator.unobtrusive.adapters.add 的 ["myMaxValue"] 對應到 html tag 的 data-val-stringlengthcheck-myMaxValue
        $.validator.unobtrusive.adapters.add("stringlengthcheck", ["myMaxValue"], function (options)
        {
            //要指定參數,在 validator 裡才接的到
            options.rules["stringlengthcheck"] = {
                myMaxValue: options.params.myMaxValue
            };
            options.messages["stringlengthcheck"] = options.message;
        });
    </script>
</head>
<body>
    <input type="text" name="myValue1" id="myValue1" 
        data-val="true" 
        data-val-stringlengthcheck="超過上限" 
        data-val-stringlengthcheck-myMaxValue=10 />
    <span data-valmsg-for="myValue1" data-valmsg-replace="true"></span>
</body>
傳入多個驗證條件
<head>
    <script>
        $.validator.addMethod('stringlengthcheck', function (value, element, params)
        {
            return value.length >= params["myValue1"] && value.length <= params["myValue2"];
        });
        //參數用 ["myValue1","myValue2"] 對應到 html tag 就是 
        //data-val-stringlengthcheck-myValue1 和 data-val-stringlengthcheck-myValue2
        $.validator.unobtrusive.adapters.add("stringlengthcheck", ["myValue1","myValue2"], function (options)
        {
            var params ={
                myValue1: options.params.myValue1,
                myValue2: options.params.myValue2
            }
            options.rules["stringlengthcheck"] = params;
            options.messages["stringlengthcheck"] = options.message;
        });        
    </script>
</head>
<body>
    <input type="text" value="" name="myValue" id="myValue" data-val="true" 
        data-val-stringlengthcheck="超過上下限"
        data-val-stringlengthcheck-myValue1=1
        data-val-stringlengthcheck-myValue2=2 />
    <span data-valmsg-for="myValue" data-valmsg-replace="true"></span>
</body>

[jQuery] validate.unobtrusive 驗證(2) 透過 Ajax (data-val-remote) 方式呼叫後端驗證

jquery.validate、jquery.validate.unobtrusive 透過 Ajax (data-val-remote) 方式呼叫後端驗證

省略部份程式碼,基本用法請參考另一篇文章

基本用法
    <body>
        <head>
            <script src="Scripts\jquery-1.10.2.js"></script>
            <script src="Scripts\jquery.validate.js"></script>
            <script src="Scripts\jquery.validate.unobtrusive.js"></script>
        </head>
        <form id="form1" method="get">

            <!-- data-val-remote 檢核失敗的訊息 -->
            <!-- data-val-remote-url 檢核網頁程式的網址 -->
            <!-- data-val-remote-type 設定 GET 或 POST -->
            <!-- data-val-remote-additionfields 要額外多傳的欄位名稱,可用","分隔指定多欄,沒有就不用給 -->

            <input type="text" name="loginName" data-val="true" 
                data-val-remote="後端回傳false時頁面上要呈現的訊息" 
                data-val-remote-url="http://localhost:14630/api/values"
                data-val-remote-type="GET" />
            <span data-valmsg-for="loginName" data-valmsg-replace="true"></span>

        </form>
    </body>

回傳的結果只能是 true/false

當指定 data-val-remote-type 為 GET 時,欄位的資料會被當作 QueryString 傳遞

只會在設定 data-val-remote 的欄位變動或 Send 時才會呼叫後端,所以當驗證需要回傳
多個欄位,而且不是每個欄位都設定 data-val-remote 的情況下,部份欄位的異動可能不
會再進行後端的檢查(因為沒設定 data-val-remote)。

以下展示呼叫及回傳的前後端程式碼

前端用 GET 呼叫後端 ASP.NET WebApi 傳遞一個參數
前端用 GET 呼叫後端 ASP.NET WebApi 傳遞三個參數
前端用 GET 呼叫後端 ASP.NET MVC 傳遞一個參數
前端用 GET 呼叫後端 ASP.NET MVC 傳遞三個參數
前端用 POST 呼叫後端 ASP.NET WebApi
前端用 POST 呼叫後端 ASP.NET MVC

前端用 GET 呼叫後端 ASP.NET WebApi 傳遞一個參數

前端
<!-- 會以如下的 uri 呼叫後端 http://localhost:12345/api/values?loginName=ABC -->

<input type="text" name="loginName" data-val="true" 
       data-val-remote="資料已存在!" 
       data-val-remote-url="http://localhost:14630/api/values"
       data-val-remote-type="GET" />
<span data-valmsg-for="loginName" data-valmsg-replace="true"></span>
後端
namespace WebApplication7.Controllers
{
    public class ValuesController : ApiController
    {
        public IHttpActionResult Get(string loginName)
        {
            bool f = true;

            if(loginName == "ABC")
            {
                f = false;
            }
            return Json<bool>(f);
         }
     }
 }

前端用 GET 呼叫後端 ASP.NET WebApi 傳遞三個參數
前端在指定 data-val-remote-additionalfields 要額外傳那些欄位時,指定的是 tag 的 name

前端
<!-- 會以如下的 uri 呼叫後端  http://localhost:12345/api/values?loginName=ab&Tel=b&Address=c -->

<input type="text" name="loginName" data-val="true" 
       data-val-remote="資料已存在!" data-val-remote-url="http://localhost:14630/api/values"
       data-val-remote-type="GET" 
       data-val-remote-additionalfields ="Tel,Address" />
<input type="text" id="idTel" name="Tel" />

<input type="text" id="idAddress" name="Address" />

<span data-valmsg-for="loginName" data-valmsg-replace="true"></span> 
後端
namespace WebApplication7.Controllers
{
    public class ValuesController : ApiController
    {
        public IHttpActionResult Get(string loginName, string Tel, string Address)
        {
            //略
            return Json<bool>(false);
        }
    }
}

前端用 GET 呼叫後端 ASP.NET MVC 傳遞一個參數

前端
<input type="text" name="loginName" data-val="true" 
    data-val-remote="資料已存在!" 
    data-val-remote-url="http://localhost:14630/home/Index"
    data-val-remote-type="GET" 
/>
<span data-valmsg-for="loginName" data-valmsg-replace="true"></span>
後端
[HttpGet]
public ActionResult Index(string loginName)
{
    //驗證
    bool b = false;
    return Json(b, JsonRequestBehavior.AllowGet);
}

前端用 GET 呼叫後端 ASP.NET MVC 傳遞三個參數

前端
<input type="text" name="loginName" 
    data-val="true" data-val-remote="資料已存在!" 
    data-val-remote-url="http://localhost:14630/home/Index"
    data-val-remote-type="GET" 
    data-val-remote-additionalfields ="Tel,Address"        
/>
<input type="text" id="idTel" name="Tel" />
<input type="text" id="idAddress" name="Address" />
<span data-valmsg-for="loginName" data-valmsg-replace="true"></span>
後端
[HttpGet]
public ActionResult Index(string loginName, string Tel, string Address)
{
    return Json(false, JsonRequestBehavior.AllowGet);
}

前端用 POST 呼叫後端 ASP.NET WebApi
使用 POST 的時候,欄位傳遞的資訊會在 POST Body 中傳遞,所以基本上後端繫結
到參數時,都要以物件去繫結。

前端
<input type="text" name="loginName"
    data-val="true" data-val-remote="資料已存在!" 
    data-val-remote-url="http://localhost:14630/api/values"
    data-val-remote-type="POST"         
    data-val-remote-additionalfields ="Tel,Address"        
/>
<input type="text" id="idTel" name="Tel" />
<input type="text" id="idAddress" name="Address" />
<span data-valmsg-for="loginName" data-valmsg-replace="true"></span>   
後端
//自訂 Class
public class UserInfo
{
    public string loginName { get; set; }
    public string Tel { get; set; }
    public string Address { get; set; }
}
public IHttpActionResult Post(UserInfo t)
{
    //驗證
    return Json<bool>(false);
}

前端用 POST 呼叫後端 ASP.NET MVC

前端
<input type="text" name="loginName" 
    data-val="true" data-val-remote="資料已存在!" 
    data-val-remote-url="http://localhost:14630/home/Index"
    data-val-remote-type="POST"         
    data-val-remote-additionalfields ="Tel,Address"        
/>
<input type="text" id="idTel" name="Tel" />
<input type="text" id="idAddress" name="Address" />
<span data-valmsg-for="loginName" data-valmsg-replace="true"></span>    
後端
//自訂 Class
public class UserInfo
{
    public string loginName { get; set; }
    public string Tel { get; set; }
    public string Address { get; set; }
}

[HttpPost]
public ActionResult Index(UserInfo ui)
{
    return Json(false);
}

2018年6月7日 星期四

[jQuery] validate.unobtrusive 驗證(1) 基本用法

環境,在 vs2013 中建立 mvc 預設專案 (就是會有預設的頁面的那種),再將其中的幾個
js 檔取出來做測試

jquery.validate 加上 validate.unobtrusive 基本上就是達成以 unobtrusive 的方
式對頁面上的欄位進行驗證。

因為是以 unobtrusive 進行,所以如何去設定欄位如何去驗證這件事情,就由原本放在
onclick 或 blur 之類的事件中,去指定並取得欄位值的方式,轉而改用設定屬性指定
要是否要驗證及驗證方式。

在使用上,有分幾個方向 (本篇是第一種)

    第一種就是只對單一欄位,以內建預設的基本檢核,比方說欄位不可為空,Email 格
    式是否正確這種。

    第二種稍微複雜一點,需要再另行決定一些「資訊」比方說,檢查欄位的值是否在某個
    區間中,這個區間就是必須要自行決定的範圍,或是需要透過正則表達式檢查欄位。

    第三種是透過 Ajax 呼叫後端進行檢查

    第四種則是自定驗證。

基本的使用

<html>
        <head>
            <script src="Scripts\jquery-1.10.2.js"></script>
            <script src="Scripts\jquery.validate.js"></script>
            <script src="Scripts\jquery.validate.unobtrusive.js"></script>
        </head>
        <body>
            <form id="form1" method="get">

                <!-- 透過設定 data-val="true" 表示要進行驗證該 tag -->
                <!-- 透過設定 data-val-required="欄位不可為空" 表示要以該欄位不可為空的條件驗證該欄位,並當檢核失敗時顯示錯誤訊息「欄位不可為空!」 -->
                <input type="text" name="username" id="username" data-val="true" data-val-required="欄位不可為空!" />

                <!-- 下面的 tag 會顯示 name 屬性為 username 的 tag 進行驗證時所產生的錯誤訊息 -->
                <!-- data-valmsg-for 是對應到受驗證欄位的 name -->
                <!-- data-valmsg-replace 為必要 (有試過有些版本的驗證不用) -->
                <span data-valmsg-for="username" data-valmsg-replace="true"></span>

                <!-- 下面的 tag 會將頁面上驗證的錯誤訊息都集中在這 -->
                <div class="validation-summary-valid" data-valmsg-summary="true">
                    <ul>
                        <li style="display: none"></li>
                    </ul>
                </div>

                <input type="submit" id="send" value="Send" />            
            </form>
        </body>
    </html>
欄位不可為空
<!-- data-val-required="欄位不能为空!" -->
<input type="text" name="uid" id="uid" data-val="true" data-val-required="欄位不能为空!" />
<span data-valmsg-for="uid" data-valmsg-replace="true"></span>

檢查 Email 格式
<!-- data-val-email="Email不正確" -->
<input type="text" name="email" id="email" data-val="true" 
       data-val-required="Email 不能為空" data-val-email="Email不正確" />
<span data-valmsg-for="email" data-valmsg-replace="true"></span>

檢查 url 格式
<!-- data-val-url="網址錯誤" -->
<input type="text" name="url" id="url" data-val="true"
       data-val-url="網址錯誤">
<span data-valmsg-for="url" data-valmsg-replace="true"></span>

日期驗證有一套規則,我是覺的不太好用,或許用自定驗證來處理會比較恰當
<!-- data-val-date="日期錯誤" -->
<input type="text" name="ndate" id="ndate" data-val="true" value=""
       data-val-date="日期錯誤">                
<span data-valmsg-for="ndate" data-valmsg-replace="true"></span>

正整數
<!-- data-val-digits="必須輸入正整數" -->
<input type="text" name="digits" id="digits" data-val="true"
       data-val-digits="必須輸入正整數">
<span data-valmsg-for="digits" data-valmsg-replace="true"></span>

檢查所輸入的是否為數字,小數可以、負數可以、整數可以
<!-- data-val-number="必須為有效數字" -->
<input type="text" name="number" id="number" data-val="true"
       data-val-number="必須為有效數字">
<span data-valmsg-for="number" data-valmsg-replace="true"></span>

比對兩個欄位的內容是否一致
 <!-- data-val-equalto 指定欄位不一致時的錯誤訊息 -->
 <!-- data-val-equalto-other 要比對的另一個欄位的 name 屬性 -->
 <body>
    <form id="form1" method="get">
        <input type="password" name="pwd" id="pwd" data-val="true" />
        <input type="password" name="pwd2" id="pwd2" data-val="true" 
               data-val-equalto="兩次輸入的密碼不一致。" data-val-equalto-other="pwd"
        />
        <span data-valmsg-for="pwd2" data-valmsg-replace="true"></span>
        <input type="submit" id="send" value="Send" />            
    </form>
</body>

檢查欄位內容的長度
在 data-val-length-max 和 data-val-length-min 間輸入欄位長度的上下限,如果沒有這
個屬性,就表示長度沒有上/下限,data-val-length 則是表示這個欄位需要檢查長度,
並且設定當欄位長度錯誤時要回應什麼錯誤訊息。
<!-- data-val-length-max="10" data-val-length-min="6" -->
    <!-- data-val-length="欄位長度需要在 6 ~ 10 個字元間" -->
    <body>
        <form id="form1" method="get">
            <input type="text" name="columnLength" id="columnLength" data-val="true" 
                data-val-length-max="10" data-val-length-min="6"
                data-val-length="欄位長度需要在 6 ~ 10 個字元間" />
            <span data-valmsg-for="columnLength" data-valmsg-replace="true"></span>
            <input type="submit" id="send" value="Send" />            
        </form>
    </body>    

2018年6月5日 星期二

[jQuery]利用 jQuery.ajax 讀取 Json 檔案

利用 jQuery.ajax 讀取 Json 檔案

環境:vs2017、asp.net mvc、jquery-1.10.2.js (專案建立時預設)

以下的範例是透過 vs2017 建立 asp.net mvc 專案 (有預設頁面的那種),建立
之後。

1.先在專案中增加 Json 資料夾
2.再修改 Home/Index.cshtml 的內容而成

使用到的 Json 檔則是放在 Json 資料夾中

testdata.json

{
    "Key1":"Value1",
    "Key2":"Value2"
}

然後在 Views\Web.config 中增加新的 mime,如果站台是架在 IIS 上,可以考慮設
定在 IIS 的 mime 上。

<system.webServer>
    <staticContent>
        <mimeMap fileExtension=".json" mimeType="application/json">
    </staticContent>
</system.webServer>

最後是頁面上寫讀取 Json 的 ajax

注意要用 GET

\Home\Index.cshtml

@{
 Layout = null;
}

<html>
<head>
 <title></title>
 <script src="~/Scripts/jquery-1.10.2.min.js"></script>
 <script>
  jQuery.ajax({
   url: '/Json/testdata.json',
   type: 'GET',
   success: function (response) {
    alert(JSON.stringify(response));
    alert(response.Key1);
    alert(response.Key2);
   }
  })
  //或是透過 jQuery.getJSON 取得 json
  jQuery.getJSON('/Json/testdata.json',
   function (data) {
    alert(JSON.stringify(data));
    alert(data.Key1);
    alert(data.Key2);
   });
 </script>
</head>
<body>
</body>
</html>

2018年3月25日 星期日

將 ASP.NET MVC 5 原始碼加入現行專案中偵錯

環境:

Win7
Visual Studio 2017 Community
.NET Framework 已安裝好 4.5.2 以上版本

(因為相依性的關係,好像必需要裝 4.5.x 以上的 .NET Framework,未測試過,僅提供參考)

google 上搜尋 「asp.net mvc 5 github」 就可以找到 asp.net mvc 5 在 github 上的連結

要注意不要選到 asp.net core mvc 的那個

進入 github 後,github 頁面上應該顯示 README.md 的內容,內容上應該會說明各個 brance 和 tag 中包含的是那個版本的 mvc,和包含了那些其他的函式庫。



此次下載的是 tag 3.2.4 的版本


開一個新的方案

加入 asp.net mvc 原始碼的專案

    加入下面 5 個現有專案 ( 位置在asp.net mvc 原始碼包的 src 資料夾中 )

    System.Web.Mvc
    System.Web.Razor
    System.Web.WebPages
    System.Web.WebPages.Deployment
    System.Web.WebPages.Razor

加入一個新的 asp.net mvc 專案


進行以下調整

先確認 5 個 mvc 專案中的參考都是正常的,如果有參考有問題先處理
通常應該是重新加入參考或是從 nuget 裡解除安裝再重新安裝就可以了。

下面例子裡可以看到 Microsoft.Web.Infrastructure 參考有問題,處
理的方式就是從 nuget 裡重裝就行了


接下來將剛剛新增的 asp.net mvc 專案中的參考移除,準備用原始碼的專案取代

移除下面 5 個參考,再透過加入專案參考的方式加入



到這一步整個方案應該是可以正常編譯成功,但是 f5 啟動網站時會出現


右鍵屬性開啟剛剛加入的 5 個 asp.net mvc 原始碼的專案,在「簽署」的頁籤中,將「簽署組件」的項目取消掉,這 5 個專案都要做一次。


都取消簽署組件之後,將 System.Web.WebPages 中的 Properties 下的 AssemblyInfo.cs 打開


將 System.Web.Mvc 的 PublicKey 移掉


打開新增的 asp.net mvc 專案,在 Views \ Web.Config 裡面的 public key 移掉


接著應該就可以利用 F12 的方式去追踨 asp.net mvc 5 的原始碼了

2018年3月18日 星期日

ASP.NET MVC FormsAuthenticationTicket 驗證(2)

自訂 FormsAuthenticationTicket

當在進入 Action 前,需要是否已經登入以外的驗證,但又不想在每個 Action 裡面都加上一段驗證行為的程式碼。

此時可以利用自訂 FormsAuthenticationTicket 的方式處理。

原因在於產生 ticket 時,可以在 ticket 中加入自訂的資訊,讓 ticket 進行驗證的同時,透過這些自訂的資訊來完成額外的驗證。

如果不想用 FormsAuthenticationTicket 也可以自己做一個繼承 Attribute 類別的Attribute 掛在 Action 上,一樣的道理

例子裡是把驗證放在 OnAuthorization 這個方法中,還有另一個 AuthorizeCore 方法也可以用。

追溯 AuthorizeAttribute 的父類別,也是有繼承 Attribute,就如同自訂的 Attribute,也可在把它掛上 Action 時,傳入參數做處理。

比方說我需要驗證,在 ticket 中所記錄的 UserData 要包含某些特定字串,這個字串是在把Attribute 放上 Method 時定義好的。

1.建立一個新的類別,並繼承 AuthorizeAttribute
    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        public string Code { get; set; }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);            

            //判斷 ticket 是否有效(授權ok)
            if (filterContext.Result is HttpUnauthorizedResult &&
                !filterContext.HttpContext.Request.IsAjaxRequest())
            {
                //不是從 Html.RenderAction 進來的才能透過 RedirectToRoute 轉向
                if (!filterContext.IsChildAction)
                {
      //失敗後跳轉至
                    filterContext.Result = new RedirectToRouteResult(
                        new System.Web.Routing.RouteValueDictionary
                        {
                            {"controller", "Login"},
                            {"action","Home3"}
                        });
                }
                return;
            }
            else
            {
                //ticket 有效(授權ok),開始進行自訂的驗證的部份
                FormsIdentity id = filterContext.HttpContext.User.Identity as FormsIdentity;
                //取得 ticket
                FormsAuthenticationTicket ticket = id.Ticket;

                //建立 ticket 時的特定資訊
                string info = ticket.UserData;

                //透過這個資訊進行額外的驗證
  info.IndexOf(this.Code);
            }
        }
    }

2.因為在驗證的過程中,已經決定當驗證失敗時要跳轉到那個頁面,所以下面的設定其實可以不用加
<system.web>
        <authentication mode="Forms">
            <forms loginUrl="Login/Index" cookieless="UseCookies" timeout="2880" />
        </authentication>
    </system.web>

3.在想要進行驗證的 Action 前加上 Attribute
    [CustomAuthorizeAttribute(Code = "ABC")]
    public ActionResult Home()
    {
        return View();
    }

4.當使用者登入後,寫入一張 ticket,讓之後進入 action 前能通過 [Authorize()] 驗證
        public ActionResult Login(string uid)
        {
            //驗證使用者輸入的帳號密碼
            if (uid.ToUpper() == "UID")
            {
                //驗證通過,建立一張 ticket
                FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                    1,                                      //票證的版本琥碼
                    uid,                                    //與票證相關的使用者名稱
                    DateTime.Now,                           //核發此票證時的本機日期和時間
                    DateTime.Now.AddMinutes(60),            //票證到期的本機日期和時間
                    false,                                  //如果票證將存放於持續性Cookie中,則為true
                    "ABC",                                  //要與票證一同存放的使用者特定資料
                    FormsAuthentication.FormsCookiePath);   //票證存放於Cookie中時的路徑

                //ticket 生成 cookie
                string encTicket = FormsAuthentication.Encrypt(ticket);
                HttpCookie httpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
                httpCookie.HttpOnly = true;

                //cookie 寫入 response
                Response.Cookies.Add(httpCookie);

                //轉向到首頁
                return View("Home");
            }
            else
            {
                //驗證不通過,返回到登錄頁
                return View("Index");
            }
        }

2018年3月14日 星期三

ASP.NET MVC FormsAuthenticationTicket 驗證

透過 FormsAuthenticationTicket 進行使用者是否登入的驗證

FormsAuthenticationTicket 此功能主要在記錄使用者是否已經登入,實際上
會產生一組 Cookie,內含一組經過加密的編碼,.net 可以透過檢核這組編碼,
來判斷使用者是否已經登入(或者說已獲得允許使用的權限)。

使用者在開啟頁面,程式進入 Action 前,先檢查使用者登入的驗證,也就是指
是否已取得 ticket,若未通過驗證,則跳轉到登錄頁,若已驗證,則允許進入
Action 內。

當使用者已經在登錄頁登錄,且檢核通過,則產生一張 ticket (也就是一組 Cookie),
當使用者切換頁面時,瀏覽器便會再把這組 ticket(Cookie),帶到後端,後端只
要檢核這組 ticket,就可以得知使用者是否仍處於已驗證的狀態。

1.在要進行驗證的 Action 前掛上 [Authorize()]
    [Authorize()]
    public ActionResult Home2()
    {
        return View();
    }
2.在 WebConfig 中加上,當驗證失敗時要導向的頁面位置(要加在專案下的那個 WebConfig)
    <system.web>
        <authentication mode="Forms">
            <forms loginUrl="Login/Index" cookieless="UseCookies" timeout="2880" />
        </authentication>
    </system.web>
3.當使用者登入後,寫入一張 ticket,讓之後進入 action 前能通過 [Authorize()] 驗證
    public ActionResult Login(string uid)
    {
        //驗證使用者輸入的帳號密碼
        if (uid.ToUpper() == "UID")
        {
            //驗證通過,建立一張 ticket
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                1,                                      //票證的版本琥碼
                uid,                                    //與票證相關的使用者名稱
                DateTime.Now,                           //核發此票證時的本機日期和時間
                DateTime.Now.AddMinutes(60),            //票證到期的本機日期和時間
                false,                                   //如果票證將存放於持續性Cookie中,則為true
                "",                                     //要與票證一同存放的使用者特定資料
                FormsAuthentication.FormsCookiePath);   //票證存放於Cookie中時的路徑

            //ticket 生成 cookie
            string encTicket = FormsAuthentication.Encrypt(ticket);
            HttpCookie httpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
            httpCookie.HttpOnly = true;

            //cookie 寫入 response
            Response.Cookies.Add(httpCookie);

            //轉向到首頁
            return View("Home");
        }
        else
        {
            //驗證不通過,返回到登錄頁
            return View("Index");
        }
    }

2018年1月2日 星期二

[Git] 中文亂碼

環境:Win7 64Bit、Git 官網上下載安裝的 Git (Git for Windows)

[Git Log 亂碼]

處理中文亂碼的部份,在 CMD 模式下輸入

#顯示status編碼

    git config --global core.quotepath false

#圖形界面編碼

    git config --global gui.encoding utf-8

#commit的時候的編碼

    git config --global i18n.commit.encoding utf-8 

#由git輸出log時候的編(但是是透過less做分頁,所以git log的時候還牽涉到less的編碼)

    git config --global i18n.logoutputencoding utf-8

以我為例,我在 C:\Program Files\Git\etc 這個路徑下,有一個 profile 檔案
開啟此檔案後在最尾巴加入下面這行設定,調整less的編碼

   export LESSCHARSET=utf-8

如果以上設定好後,在 git log 指令仍然會輸出亂碼時 (大概長的像這樣 <E1><F1><C1><D1>),
則在 CMD 模式下輸入

    SETX LC_ALL C.UTF-8

多建立一個環境變數,好讓 Git for Windows 內建的 git.exe,能夠讀取到 LC_ALL 這個變數,
從而判斷怎麼顯示文字。