2017年10月16日 星期一

C# HttpClient WebAPI : 1. 研究專題介紹

這是一篇關於如何在用戶端 (Client) 使用 HttpClient 類別的說明系列文章,在這裡我們將會說明如何使用 HttpClient 所建立的物件,進行遠端 Web API 的 Get / Post / Put / Delete 等動作的呼叫與接收回傳結果,當然,我們也會說明如何使用 QueryString / Form-Data / Multi-Part / JSON 等不同格式,將用戶端的資料,傳遞到遠端 Web API 的使用方式;除了一般純文字的 Web API 內容呼叫與回傳,我們在這裡也會進行如何處理 Binary 二進位的資料上傳與取得,我們將會說明如何上傳圖片檔案到遠端 Web API 伺服器上與如何使用 HttpClient 取得遠端 Web Server 上的圖片檔案,接著儲存到本機上。
當然,我們也需要了解如何從用戶端傳送 Http Header 與 Cookie 到遠端 Web API上,接著,如何在用戶端中,取得 Web API回報的 Cookie 與 Header 資料;尤其,我們也會展示如何顯示 HttpClient 下載大量資料的時候的進度回報事件設計方式與如何取消較長時間的 HttpClient 呼叫,最後,我們將會設定一個逾時時間,當執行 HttpClient 的 Web API 執行期間,花費時間超過我們所設定的時間,將會終止這樣的 Web API 呼叫之程式寫法。
這篇系列文章的所有使用到的專案原始碼,您可以從 https://github.com/vulcanlee/CSharpNotes/tree/master/WebAPI 取得

了解更多關於 [HttpClient Class] 的使用方式
了解更多關於 [使用 async 和 await 進行非同步程式設計] 的使用方式


HttpClient 系列文章索引


      1. +
      2. C# HttpClient WebAPI : 13. 上傳本機圖片檔案到遠端伺服器上
      3. C# HttpClient WebAPI : 17. 有趣的 HttpClient 管道,自訂 HttpMessage Handler
      4. C# HttpClient WebAPI : 18. 套用 ProgressMessageHandler ,訂閱請求與回應的資料傳輸事件

        後端 Web API 的原始碼

        底下將會是我們要使用到的 Web API 專案原始碼,在這裡,我們採用的是 ASP.NET Core Web API 類型專案來開發,並且將這個 Web API 專案佈署到 Azure 平台上。在每個 HttpClient 研究文章中,將也會針對所使用的後端 Web API 方法,進行進一步的說明。
        底下是絕大部分的 Web API 控制器 ValuesController 的程式碼:
        namespace VulcanWebAPI.Controllers
        {
            [Route("api/[controller]")]
            public class ValuesController : Controller
            {
                // http://vulcanwebapi.azurewebsites.net/api/values
                [HttpGet]
                public APIResult Get()
                {
                    APIResult foo = new APIResult()
                    {
                        Success = true,
                        Message = "成功得所有資料集合",
                        Payload = new List<APIData>()
                        {
                            new APIData()
                            {
                                Id =777,
                                Name = "Vulcan01"
                            },
                            new APIData()
                            {
                                Id =234,
                                Name ="Vulcan02"
                            }
                        }
                    };
                    return foo;
                }
        
                [HttpGet("QueryStringGet")]
                public APIResult QueryStringGet([FromQuery] APIData value)
                {
                    APIResult foo;
                    if (value.Id == 777)
                    {
                        foo = new APIResult()
                        {
                            Success = true,
                            Message = "透過 Get 方法,接收到 Id=777",
                            Payload = new APIData()
                            {
                                Id = 777,
                                Name = "Vulcan by QueryStringGet"
                            }
                        };
                    }
                    else
                    {
                        foo = new APIResult()
                        {
                            Success = false,
                            Message = "無法發現到指定的 ID",
                            Payload = null
                        };
                    }
                    return foo;
                }
        
                [HttpGet("GetException")]
                public APIResult GetException()
                {
                    APIResult foo = new APIResult();
                    throw new Exception("喔喔,我發生錯誤了");
                    return foo;
                }
        
                [HttpGet("GetExceptionFilter")]
                [CustomExceptionFilter]
                public APIResult GetExceptionFilter()
                {
                    APIResult foo = new APIResult();
                    throw new Exception("喔喔,我發生錯誤了");
                    return foo;
                }
        
                // http://vulcanwebapi.azurewebsites.net/api/values/777
                [HttpGet("{id}")]
                public APIResult Get(int id)
                {
                    APIResult foo;
                    if (id == 777)
                    {
                        foo = new APIResult()
                        {
                            Success = true,
                            Message = "透過 Get 方法,接收到 Id=777",
                            Payload = new APIData()
                            {
                                Id = 777,
                                Name = "Vulcan01"
                            }
                        };
                    }
                    else
                    {
                        foo = new APIResult()
                        {
                            Success = false,
                            Message = "無法發現到指定的 ID",
                            Payload = null
                        };
                    }
                    return foo;
                }
        
                // http://vulcanwebapi.azurewebsites.net/api/Values
                // 使用 JSON 格式
                [HttpPost]
                public APIResult Post([FromBody]APIData value)
                {
                    APIResult foo;
        
                    if (value.Id == 777)
                    {
                        foo = new APIResult()
                        {
                            Success = true,
                            Message = "透過 post 方法,接收到 Id=777 資料",
                            Payload = value
                        };
                    }
                    else
                    {
                        foo = new APIResult()
                        {
                            Success = false,
                            Message = "無法發現到指定的 ID",
                            Payload = null
                        };
                    }
                    return foo;
                }
        
                // http://vulcanwebapi.azurewebsites.net/api/Values/FormUrlencodedPost
                // 使用 FormUrlEncodedContent
                [HttpPost("FormUrlencodedPost")]
                public APIResult FormUrlencodedPost([FromForm]APIData value)
                {
                    APIResult foo;
        
                    if (value.Id == 777)
                    {
                        foo = new APIResult()
                        {
                            Success = true,
                            Message = "透過 post 方法,接收到 Id=777 資料",
                            Payload = value
                        };
                    }
                    else
                    {
                        foo = new APIResult()
                        {
                            Success = false,
                            Message = "無法發現到指定的 ID",
                            Payload = null
                        };
                    }
                    return foo;
                }
        
                [HttpPut]
                public APIResult Put(int id, [FromBody]APIData value)
                {
                    APIResult foo;
        
                    if (value.Id == 777)
                    {
                        foo = new APIResult()
                        {
                            Success = true,
                            Message = "透過 Put 方法,接收到 Id=777 資料",
                            Payload = value
                        };
                    }
                    else
                    {
                        foo = new APIResult()
                        {
                            Success = false,
                            Message = "無法發現到指定的 ID",
                            Payload = null
                        };
                    }
                    return foo;
                }
        
                [HttpDelete("{id}")]
                public APIResult Delete(int id)
                {
                    APIResult foo;
        
                    if (id == 777)
                    {
                        foo = new APIResult()
                        {
                            Success = true,
                            Message = "Id=777 資料 已經刪除了",
                            Payload = null
                        };
                    }
                    else
                    {
                        foo = new APIResult()
                        {
                            Success = false,
                            Message = "無法發現到指定的 ID",
                            Payload = null
                        };
                    }
                    return foo;
                }
        
                // http://vulcanwebapi.azurewebsites.net/api/values/HeaderPost
                [HttpPost("HeaderPost")]
                public APIResult HeaderGet([FromBody]LoginInformation loginInformation)
                {
                    APIResult foo;
                    StringValues VerifyCode = "";
        
                    this.HttpContext.Request.Headers.TryGetValue("VerifyCode", out VerifyCode);
                    if (StringValues.IsNullOrEmpty(VerifyCode))
                    {
                        foo = new APIResult()
                        {
                            Success = false,
                            Message = "驗證碼沒有發現",
                            Payload = null
                        };
                    }
                    else
                    {
                        if (VerifyCode != "123")
                        {
                            foo = new APIResult()
                            {
                                Success = false,
                                Message = "驗證碼不正確",
                                Payload = null
                            };
                        }
                        else
                        {
                            if (loginInformation.Account == "Vulcan" &&
                                loginInformation.Password == "123")
                            {
                                foo = new APIResult()
                                {
                                    Success = true,
                                    Message = "這個帳號與密碼正確無誤",
                                    Payload = null
                                };
                            }
                            else
                            {
                                foo = new APIResult()
                                {
                                    Success = false,
                                    Message = "這個帳號與密碼不正確",
                                    Payload = null
                                };
                            }
                        }
                    }
                    return foo;
                }
        
                [HttpPost("Login")]
                public async Task<APIResult> Login([FromBody]LoginInformation loginInformation)
                {
                    APIResult foo;
        
                    if (loginInformation.Account == "Vulcan" &&
                        loginInformation.Password == "123")
                    {
                        var claims = new List<Claim>() {
                        new Claim(ClaimTypes.Name, "Herry"),
                        new Claim(ClaimTypes.Role, "Users")
                    };
                        var claimsIdentity = new ClaimsIdentity(claims, "myTest");
                        var principal = new ClaimsPrincipal(claimsIdentity);
                        try
                        {
                            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
                                principal,
                                new AuthenticationProperties
                                {
                                    ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
                                    IsPersistent = true,
                                    AllowRefresh = true
                                });
                            foo = new APIResult()
                            {
                                Success = true,
                                Message = "這個帳號與密碼正確無誤",
                                Payload = null
                            };
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                            foo = new APIResult()
                            {
                                Success = false,
                                Message = "這個帳號與密碼不正確",
                                Payload = null
                            };
                        }
                    }
                    else
                    {
                        foo = new APIResult()
                        {
                            Success = false,
                            Message = "這個帳號與密碼不正確",
                            Payload = null
                        };
                    }
        
                    return foo;
                }
        
                [Authorize]
                [HttpGet("LoginCheck")]
                public APIResult LoginCheck()
                {
                    APIResult foo = new APIResult()
                    {
                        Success = true,
                        Message = "成功得所有資料集合",
                        Payload = new List<APIData>()
                        {
                            new APIData()
                            {
                                Id =777,
                                Name = "Vulcan01"
                            },
                            new APIData()
                            {
                                Id =234,
                                Name ="Vulcan02"
                            }
                        }
                    };
                    return foo;
                }
        
                [HttpGet("LongTimeGet")]
                public async Task<APIResult> LongTimeGet()
                {
                    APIResult foo;
        
                    await Task.Delay(5000);
                    foo = new APIResult()
                    {
                        Success = true,
                        Message = "透過 Get 方法",
                        Payload = new APIData()
                        {
                            Id = 777,
                            Name = "Vulcan01"
                        }
                    };
                    return foo;
                }
        
            }
        }
        
        底下將是圖片上傳的 UploadController 控制器原始碼:
        namespace VulcanWebAPI.Controllers
        {
            [Route("api/[controller]")]
            public class UploadController : Controller
            {
                APIResult fooAPIResult = new APIResult();
                private IHostingEnvironment _HostingEnvironment;
        
                public UploadController(IHostingEnvironment hostingEnvironment)
                {
                    _HostingEnvironment = hostingEnvironment;
                }
        
                [HttpPost]
                public async Task<APIResult> Post(List<IFormFile> files)
                {
                    // https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads
        
                    string webDatasRoot = Path.Combine(_HostingEnvironment.WebRootPath, "Datas");
        
                    long size = files.Sum(f => f.Length);
        
                    // full path to file in temp location
        
                    if (files.Count > 0)
                    {
                        foreach (var formFile in files)
                        {
                            if (formFile.Length > 0)
                            {
                                var filePath = Path.Combine(webDatasRoot, formFile.FileName);
                                using (var stream = new FileStream(filePath, FileMode.Create))
                                {
                                    await formFile.CopyToAsync(stream);
                                }
        
                                fooAPIResult.Success = true;
                                fooAPIResult.Message = "檔案上傳成功";
                                fooAPIResult.Payload = new APIData
                                {
                                    Id = 3000,
                                    Name = "Your Name",
                                    Filename = formFile.FileName
                                };
                            }
                        }
                    }
                    else
                    {
                        fooAPIResult.Success = false;
                        fooAPIResult.Message = "沒有任何檔案上傳";
                        fooAPIResult.Payload = null;
                    }
        
                    return fooAPIResult;
                }
        
        
                [HttpPost("FileAndData")]
                public async Task<APIResult> FileAndData(List<IFormFile> files, LoginInformation loginInformation)
                {
                    // https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads
        
                    string webDatasRoot = Path.Combine(_HostingEnvironment.WebRootPath, "Datas");
        
                    long size = files.Sum(f => f.Length);
        
                    // full path to file in temp location
                    if (files.Count > 0)
                    {
                        foreach (var formFile in files)
                        {
                            if (formFile.Length > 0)
                            {
                                using (var memoryStream = new MemoryStream())
                                {
                                    await formFile.CopyToAsync(memoryStream);
                                    memoryStream.Seek(0, SeekOrigin.Begin);
                                    var streamReader = new StreamReader(memoryStream);
                                    var fooContent = streamReader.ReadToEnd();
        
                                    fooAPIResult.Success = true;
                                    fooAPIResult.Message = "檔案上傳成功";
                                    fooAPIResult.Payload = new LoginInformation
                                    {
                                        Account = $">> {loginInformation.Account}",
                                        Password = $">> {loginInformation.Account}",
                                        VerifyCode = fooContent
                                    };
                                }
                            }
                        }
                    }
                    else
                    {
                        fooAPIResult.Success = false;
                        fooAPIResult.Message = "沒有任何檔案上傳";
                        fooAPIResult.Payload = null;
                    }
        
                    return fooAPIResult;
                }
            }
        }

        了解更多關於 [HttpClient Class] 的使用方式
        了解更多關於 [使用 async 和 await 進行非同步程式設計] 的使用方式

        關於 Xamarin 在台灣的學習技術資源

        Xamarin 實驗室 粉絲團
        歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
        Xamarin.Forms @ Taiwan
        歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
        Xamarin 實驗室 部落格
        Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
        Xamarin.Forms 系列課程
        Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程



        沒有留言:

        張貼留言