重溫ASP.NET WebAPI(二)進階


介紹

本文為個人對WebApi的回顧無參考價值。

本文內容:

  1. Rest和UnitOfWork
  2. 創建WebAPi的流程
  3. IOC-Unity的使用
  4. MEF
  5. 自定義URL
  6. Base認證和Token自定義權限
  7. 日志NLog
  8. OData的使用
  9. Owin自宿主的使用

代碼地址:https://github.com/OtherRuan/Review-Serials

WebApi的幾點特性

WebApi 提供了幾點特性:

1. 自動匹配HTTP方法

GetMethod(), 慣例上會直接匹配Get的http方法。

Web Api允許方法同時擁有多種type,比如:

        [AcceptVerbs(“Delete”,”Get”)]

        Public HttpResponseMessage MultiMethod();

        MultiMethod可以同時為Delete、Get兩種類型的http請求使用

2. Web Api還提供自定義Route的功能,比如定義自己的參數,如下

                [Route(“data/{param1}/{param2}”)]

                Public object GetData(string param1, string param2){}

                請求地址:http://localhost/data/1/2

Route屬性有以下5種,用於幫你重定義你的API路由

a)      ActionName: 定義你自己的路由的Action名稱

b)      Http Method(httpGet,HttpPost,AcceptVerbs…):  定義你的HTTP方法類型和版本等相關信息

c)       NonAction: 預防當前的Action沒有被調用

d)      Route: 自定義路由,可設置參數

e)      RoutePrefix:在controller上定義前綴,Controller下的所有Action路由都會自動帶上此前綴

3. HttpClient, HttpRequestMessage, HttpResponseMessage

Https下的Web Api

Https/SSL  是管理計算機互聯網消息資源傳輸安全的協議。先介紹一下兩者

Certificate :也就是電子證書,是一種電子簽名,它通過綁定公鑰來確認用戶、設備、服務,以及對應的私鑰

Certificate Authority(CA):主要用途是指定哪些URL是可信任的URL

Repository和Unit of Work

Repository模式好處:

  1. 集中了數據和web服務訪問邏輯
  2. 支持單元測試
  3. 提供了靈活架構以適應整個應用設計的擴展

Unit of Work的職責:

  1. 管理事務
  2. 有序化數據的插入、刪除、更新操作
  3. 預防重復的更新。

使用Unit of Work模式的好處是能夠讓你更關注於業務邏輯。

創建WebAPI項目流程

  1. 創建DAL
  2. 創建Repository和Unit of Work
  3. 創建Entities
  4. 創建Services
  5. 創建WebAPI
  6. 創建IOC
  7. 使用MEF解耦依賴注冊關系
  8. 自定義路由
  9. 創建Exception處理和日志功能
  10. 創建單元測試

Questions:

  1. Context.Entry(entity).State = EntityState.Modified;
  2. IQueryable
  3. UnitOfWork繼承IDisposable

Cause:他需要釋放鏈接

  1. DbEntityValidationException e.EntityValidationErrors
  2. GC.SuppressFinalize

IOC – Unity

Unity 是輕量級、可擴展的依賴注入容器,支持構造函數注入、屬性注入和方法調用注入。

Unity的優勢:

  1. 提供簡單的對象創建,特別是分層對象結構和依賴。
  2. 提供抽象需求。允許開發人員在運行時或者配置中指定依賴。
  3. 增加靈活性,推遲組件配置到容器中
  4. 它有一個本地服務能力。允許用戶保存或緩存容器。這個特別適用於Asp.Net web應用程式中,持久化Session和application容器

創建流程:

  1. 安裝Unity for MVC
  2. 創建Bootstrapper.cs文件
    1. Initialise()

注冊對應反轉依賴

  1. Application_Start()

Bootstrapper.Initialise();

  2. 構造函數注入

private IEmployeeService _employeeService;

        public UserController(IEmployeeService employeeService)

        {

            _employeeService = employeeService;

        }

MEF(Managed Extensibility Framework)

雖然之前IOC以前減少了部分依賴,但是Domain Model依然依賴在API中。

輕耦合架構需要做到以下幾點:

  1. Domain Model:只跟Service層關聯
  2. Services:只跟REST終端和Domain Model關聯
  3. REST API,也就是Controller,通過IOC,跟Services暴露的接口關聯

為了解決API依賴Domain Model,我們采用MEF進行解耦。

MEF(Managed Extensibility Framework)是一個用於創建可擴展的輕型應用程序的庫。 應用程序開發人員可利用該庫發現並使用擴展,而無需進行配置。 擴展開發人員還可以利用該庫輕松地封裝代碼,避免生成脆弱的硬依賴項。 通過 MEF,不僅可以在應用程序內重用擴展,還可以在應用程序之間重用擴展。(摘自MSDN)

流程:

  1. 刪除原有容器的注冊配置
    container.RegisterType<IEmployeeService, EmployeeBusinessLayer>()
                        .RegisterType<UnitOfWork>(new HierarchicalLifetimeManager());

  2. 創建Resolver類庫

    a. 添加Unity.MVC

    b. 添加引用:System.ComponentModel.Composition

      這個DLL是MEF的一部分,提供MEF的核心類

    c. 添加接口IComponent

      包含方法initialization方法Setup, 組合IRegisterComponent

    d. 添加接口IRegisterComponent

      定義RegisterType等容器的方法

    e. 添加ComponentLoder加載類。

      包含LoadContainer,加載指定path路徑的dll里的所有帶有Export屬性並繼承IComponent的modules,執行他們的setup方法也就是注冊依賴關系。

var dirCat = new DirectoryCatalog(path, pattern);
            var importDef = BuildImportDefinition();
            try
            {
                using (var aggregateCatalog = new AggregateCatalog())
                {
                    aggregateCatalog.Catalogs.Add(dirCat);
                    using (var componsitionContainer = new CompositionContainer(aggregateCatalog))
                    {
                        var exports = componsitionContainer.GetExports(importDef);
                        var modules = exports.Select(export => export.Value as IComponent).Where(m => m != null);
                        var registerComponent = new RegisterComponent(container);
                        foreach (var module in modules)
                        {
                            module.SetUp(registerComponent);
                        }
                    }
                }

    f. 每個需要做IOC的類庫下,添加類DependencyResolver,繼承IComponent,實現SetUp方法,方法內部實現RegisterType功能,達到依賴注冊的作用。添加屬性Export

[Export(typeof(IComponent))]
    public class DependencyResolver : IComponent
    {
        public void SetUp(IRegisterComponent registerComponent)
        {
            registerComponent.RegisterType<IUnitOfWork, UnitOfWork>();
        }
    }

  3. 修改原來配置為如下:

ComponentLoader.LoadContainer(container, ".\\bin", "Services.dll");

使用MEF的好處:

  1. 使應用程序更解耦和可擴展。擴展的時候,只需要以同樣的方法添加新的DependencyResolver類,沒有依賴。
  2. 依賴注冊通過反射自動生成。只需要指定對應的dll的位置,如在bin下。
  3. 數據庫事務或者一些模塊不想暴露到服務終端時,MEF就變得更安全且不破壞當前設計結構。

使用AttributeRouting重寫自定義URL

流程:

  1. 在WebConfig的Register方法中,替換為MapHttpAttributeRoute();如下:
public static class WebApiConfig

    {

        public static void Register(HttpConfiguration config)

        {

            //config.Routes.MapHttpRoute(

            //    name: "DefaultApi",

            //    routeTemplate: "api/{controller}/{id}",

            //    defaults: new { id = RouteParameter.Optional }

            //);

            config.MapHttpAttributeRoutes();

        }

    }

  2. Global.ascx中,替換原來的注冊

protected void Application_Start()

        {

            //WebApiConfig.Register(GlobalConfiguration.Configuration);

            GlobalConfiguration.Configure(WebApiConfig.Register);

}

  3. 幾種Route自定義方式

    a. Controller上設置前綴

[RoutePrefix("users/user")]

[RoutePrefix("v1/users/user")]

    b. Action上自定義路由

[Route("u/{id?}")]

[Route("u/{id:range(1,3)}")]

[Route("u/id/{e:regex(^[0-9]$)}")]

[Route("~/myroute/users")]

使用ActionFilter創建基於WebApi認證安全和基於Token的自定義權限

企業級應用的安全尤為重要,特別是通過服務暴露我們的業務數據。先介紹一下內容:

  1. Authentication認證

Authentication認證用於確認終端用戶,驗證用戶是否有權限訪問系統。等會通過Basic Authentication技術來理解如何在webapi中實現authentication功能。

  1. Authorization授權

Authorization授權可以理解為做完Authentication認證后的第二部實現安全機制。並不是所有可以訪問系統的用戶都能訪問所有模塊比如action。Authorization通過設置角色和許可給終端用戶,或者通過提供安全的token,來指定用戶是否能夠訪問具體系統資源。

  1. 持久化Session

RESTful服務工作在無狀態的協議,比如HTTP。我們可以通過基於token授權技術來實現持久化Session的功能。一個授權過的用戶,允許你在一定時間內訪問資源,且能通過延長session的有效時間來重新實例化請求訪問資源。使用WebAPI的站點可以通過Basic Authentication和Token Base authorization來持久化session.

Basic Authentication

Basic認證是一種機制。當用戶請求服務時,會將用戶名密碼等嵌套在請求頭。服務接收請求后驗證證書是否有效,然后返回響應結果。無效證書對應的響應結果是401,代表無權限訪問。

優點:容易實現,支持所有瀏覽器,並且成為RESTful的標准認證。

缺點:用戶證書包含在請求頭,很容易受到攻擊。沒有持久化Session,一旦用戶登錄且多次發送過證書給服務的時候,就不能退出。而且非常容易受到攻擊,如CSRF

基於Token授權

Token一般為加密后的key,只有服務器或者服務知道它的含義。當用戶發送請求並傳遞token的時候,服務器通過token判斷用戶是否有權限訪問系統。生成后的Token可以被存到數據庫或者配置文件中。Token有自己的生命周期,有失效時間。

WebAPI使用Basic認證和Token授權的流程:

  1. 創建用戶表
  2. 創建Service和Repository
  3. IOC
  4. 創建AuthorizationFilterAttribute
    1. 實現OnAuthrozation

                      i.      獲取用戶相關信息GenericIdentity

filterContext.Request.Headers.Authorization.

Scheme==”Basic”

Parameter.split(“:”) [0] username, [1]password

                      ii.      調用Service獲取數據庫用戶信息,進行驗證

  5. 通過添加屬性過濾,或者在global添加全局過濾

[ApiAuthenticationFilter]

GlobalConfiguration.Configuration.Filters.Add(new ApiAuthenticationFilter());

設計缺陷

每一次請求都發送用戶密碼。假設我創建一個應用,這個應用的認證只在我登錄的時候發生一次。這時我也有權限訪問其他服務。我們的應用應該更安全,他要能約束及時認證過的用戶,不能訪問沒有授權給他的服務。

通過Token來實現授權。只暴露比如登錄的服務給用戶。當用戶登錄成功后,發送一個token(可以是GUID或者加密的key)給用戶,每一次請求時都要帶上這個token。在持久化session方面,token有失效時間,一般為15分鍾,可以在web.config做個入口配置。在session過期后,用戶登出,重新登錄獲取新的token。

使用Action Filter, Exception Filter實現WebAPI的日志容錯功能

NLog的使用

  1. Nuget下載NLog
  2. 配置NLog
    1. ConfigSection的配置

      2. NLog節點的配置

  3. Web項目中添加APILog文件夾

  4. 添加Helpers文件夾,添加以下幾個文件

    1. NLogger類

      繼承ITraceWriter的Trace方法,主要用於記錄所有類型的錯誤和信息的日志。

  5. 添加LoggingFilterAttribute繼承Action Filter

    將NLogger類引入

  6. 注冊LoggingFilterAttribute到Global

錯誤日志

  1. 添加GlobalExceptionAttribute類,繼承ExceptionFilterAttribute,實現OnException方法

將NLogger注入

  2. 其余操作如日志一樣

自定義錯誤日志

可以將錯誤分為三大類:API級別的錯誤、業務錯誤、數據錯誤

  1. 添加接口IApiException

    包含屬性Error的基本信息屬性

  2. 各添加三個類ApiException、ApiBusinessException、ApiDataException,繼承IApiException和Exception

  3. 引用JSon序列化

    Log需要能夠序列化成json以便我們能夠將日志對象傳輸到各個模塊中去。

  4. 修改之前的NLogger類

  5. 修改GlobalExceptionAttribute

  6. 在Controller中,拋出我們自定義的錯誤類

WebAPI中的OData

OData是一種協議,它提供靈活創建可查詢的REST服務,准確的說,它提供各種查詢選項,如參數,來決定你具體要查詢的數據。如下面鏈接

http://localhost/Products?$orderby=Name

OData允許你創建可查詢的服務,當終端服務支持OData時,你可以過濾請求結果,比如提取前n條數據,排序,選取某條數據等等。

查詢選項

ASP.NET WebAPI支持以下幾種:

  1. $orderby:排序
  2. $select:選擇某列或者某個屬性
  3. $skip:與Linq類似,跳過前N條數據,提取N+1后數據
  4. $top:前N條
  5. $expand:擴展實體
  6. $filter:過濾
  7. $inlinecount:類似分頁

使用流程

  1. nuget安裝OData
  2. 在Api里的結果集中引用方法AsQueryable(),設置成可查詢
_employeeService.GetAll().AsQueryable()

  3. 各種查詢選項的使用

    a. $top

    /Employee/All?$top=2

    b. $filter

    /Employee/All?$ filter =name eq ‘Ryan ’  獲取name等於Ryan的數據

    /Employee/All?$ filter =id lt 3  獲取id小於3的數據

    c. $orderby

    /Employee/All?$ orderby =name desc

    d. $orderby和 $top一起用

    /Employee/All?$top=2&orderby = name desc

    e. $skip

    /Employee/All?$top=5$skip=2 取第3條開始的前5條數據,3-7

Filter的操作符

eq

等於

$filter=revenue eq 100

ne

不等於

$filter=revenue ne 100

gt

大於

$filter=revenue gt 100

ge

大於等於

$filter=revenue ge 100

lt

小於

$filter=revenue lt 100

le

小於等於

$filter=revenue le 100

and

$filter=revenue lt 100 and revenue gt 2000

Or

$filter=contains(name,'(sample)') or contains(name,'test')

Not

不包含

$filter=not contains(name,'sample')

( )

括號域

(contains(name,'sample') or contains(name,'test')) and revenue gt 5000

查詢函數

Contains

$filter=contains(name,'(sample)')

endswith

$filter=endswith(name,'Inc.')

startswith

$filter=startswith(name,'a')

    f. 分頁

    添加屬性[Queryable(PageSize = 10)]

    g. 設置可允許的選項

 [Queryable(AllowedQueryOptions =AllowedQueryOptions.Filter | AllowedQueryOptions.OrderBy)]

    h. 允許具體的排序范圍

[Queryable(AllowedOrderByProperties = "ProductId")]

    i. 允許操作符范圍

[Queryable(AllowedLogicalOperators = AllowedLogicalOperators.GreaterThan)]

    j. 允許計算操作符的范圍

[Queryable(AllowedArithmeticOperators = AllowedArithmeticOperators.Add)]

創建自宿主的WebAPI-OWIN

  1. 添加console應用
  2. Nuget安裝webapi owin self-host
  3. 添加api
  4. 添加Startup類

    使用httpConfiguration創建路由,通過appBuilder.UseWebApi方法添加到請求管道中

   

  5.Main函數中使用Api項目里的Startup類

 

  6. 運行console

參考文獻

http://www.codeproject.com/Articles/659131/Understanding-and-Implementing-ASPNET-WebAPI

http://www.codeproject.com/Articles/838274/Web-API-Thoughts-of-Data-Streaming

http://www.codeproject.com/Articles/990492/RESTful-Day-sharp-Enterprise-Level-Application#_Toc418969124

http://www.codeproject.com/Articles/889242/WebAPI-Self-Hosting-Using-OWIN

http://www.codeproject.com/Articles/631668/Learning-MVC-Part-Repository-Pattern-in-MVC-App

http://www.codeproject.com/Articles/640294/Learning-MVC-Part-Generic-Repository-Pattern-in


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM