c# webapi2 實用詳解

本文介紹webapi的使用知識

  1. 發佈webapi的問題前端

    配置問題
        webapi的項目要前端訪問,須要在web.config配置文件中添加以下配置
        在system.webServer節點下面添加
            <modules runAllManagedModulesForAllRequests="true" />
            <httpProtocol>
            <customHeaders>
                <add name="Access-Control-Allow-Origin" value="*" />
                <add name="Access-Control-Allow-Headers" value="*" />
                <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
            </customHeaders>
            </httpProtocol>
        在Global.asax.cs文件中添加
            GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
  2. 最基礎的demoios

    public class ProductsController : ApiController
    {
        MyTest[] tests = new MyTest[]
        {
            new MyTest {Id = 1, Name = "蘋果", Category = "水果", Price = 10},
            new MyTest {Id = 2, Name = "番茄", Category = "蔬菜", Price = 5},
            new MyTest {Id = 3, Name = "豬肉", Category = "肉類", Price = 20}
        };
        public IEnumerable<MyTest> GetAllProducts()
        {
            return tests;
        }
        public IHttpActionResult GetProduct(int id)
        {
            var product = tests.FirstOrDefault(p => p.Id == id);
            if(product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }
    }
    將程序編譯好,而後發佈出去就能夠經過以下方式請求數據
    var axiosInstance = window.axios.create({
        baseURL: "http://192.168.31.198:8888/api",
        timeout: 5000
    })
    // 或者是products/2 獲取具體某一條數據
    axiosInstance.get("products").then(function (res) {
        debugger
    }).catch(function (err) {
        console.log(err);
    })
  3. 控制器中方法的返回值web

    返回void 
        若是一個get方法寫成
            public void GetAllProducts(){}
            那麼HTTP響應狀態碼是204,表示沒有返回內容
    返回HttpResponseMessage
        若是返回的是HttpResponseMessage對象,那麼請求的HTTP響應信息就是HttpResponseMessage對象
        public HttpResponseMessage GetAllProducts()
        {
            // 建立HttpResponseMessage對象,而且將返回值設置爲 葉家偉
            HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "葉家偉"); 
            // 設置響應的返回內容,StringContent用來返回字符串的Json對象
            response.Content = new StringContent(@"{""a"": ""1""}", Encoding.UTF8); 
            // 設置響應頭的緩存控制
            response.Headers.CacheControl = new CacheControlHeaderValue
            {
                NoCache = false
            };
            return response;
        }
        能夠直接設置  HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, tests);
        將tests可枚舉對象做爲返回結果
    返回IHttpActionResult
        IHttpActionResult是HttpResponseMessage的生產工廠
        若是一個控制器的方法返回一個IHttpActionResult,那麼會自動調用ExecuteAsync方法建立HttpResponseMessage
        ApiController提供了不少的內置生產IHttpActionResult類型的函數,方便調用,好比OK,NotFound等
    其餘類型的返回值    
        用戶能夠返回值類型和引用類型,web api使用媒體序列化器解析返回值類型,默認支持的數據格式有
        xml,json,bson,form-urlencoded,若是有其餘類型須要本身手動的建立
        public MyTest[] GetAllProducts()
        {
            return tests;
        }
  4. 建立webapi文檔頁正則表達式

    首先,安裝 Install-Package Microsoft.AspNet.WebApi.HelpPage
    而後,在Global.asax文件中的Application_Start方法下添加
        AreaRegistration.RegisterAllAreas();
    而後,在Areas/HelpPage/App_Start/HelpPageConfig.cs,開啓
        config.SetDocumentationProvider(new XmlDocumentationProvider(
        HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
    在項目屬性的生成中開啓XML documentation file 添加 App_Data/XMLDocument.xml
    最後發佈,將相關的資源包含到項目中來,而後生成,完事兒了發佈便可
    剩下的就是寫註釋了
  5. 路由json

    控制器中的方法能夠使用 GET,POST,PUT和DELETE 開頭的命名規則,來指定具體請求對應的方法
    固然,你也能夠選擇修飾符的方式來指定方法,支持的修飾符 HttpGet, HttpPut, HttpPost, or HttpDelete
        [HttpPost]
        public MyTest[] AllProducts()
        {
            return tests;
        }
        使用 [AcceptVerbs("GET", "HEAD")] 這種寫法,能夠支持多種http請求,而且還能夠使用GET,POST,PUT和DELETE以外的方式
    另外若是你想把具體的方法的名稱也加到請求的url中,那麼能夠配合上面的修飾符形式,結合下面的方法實現
        首先,將WebApiConfig.cs文件中的配置改爲
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
            還能夠這樣配置
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{action}/{id}",
                defaults: new { controller = "Department", id = RouteParameter.Optional }
            ); // 表示若是api知足routeTemplate那麼使用Department控制器匹配,若是不知足則執行其餘的路由匹配規則
        控制器中的方法的寫法
            [HttpPost]
            public MyTest[] AllProducts()
            {
                return tests;
            }
        url的請求方式
            post("products/AllProducts")
    若是,想無視某個方法,添加 [NonAction] 便可
  6. 路由配置axios

    路由模板,也就是路由配置中的routeTemplate選項
        "api/{controller}/yejiawei/{action}/{id}"
        其中使用花括號包裹的是佔位符,能夠有多個佔位符
    路由參數默認值,是路由配置中的defaults參數,用來給方法的參數指定默認值
        new { id = RouteParameter.Optional } 表示id這個字段在匹配的時候能夠有,也能夠沒有
        new { id = 2 } 表示id這個字段能夠有也能夠沒有,可是若是沒有會自動設置爲2
        new { id = @"\d+" } 能夠使用正則限定id能夠的值
    建立路由匹配規則的方式
        除了使用config.Routes.MapHttpRoute方法建立路由規則外,還能夠使用以下的方式
            IHttpRoute myRoute = config.Routes.CreateRoute(
                "api/{controller}/yejiawei/{id}",
                new { id = RouteParameter.Optional },
                null
            );
            config.Routes.Add("MyRoute", myRoute);
        MapHttpRoute方法的參數除了上面將到的name,routeTemplate,defaults還有以下的參數
            constraints 使用正則表達式,限定傳參的要求
                constraints: new { id = @"/d+"}
            handler 每一個請求的分發函數
    路由字典
        var routeData = Request.GetRouteData();
        IDictionary<string, object> routes = routeData.Values;
        foreach(KeyValuePair<string, object> item in routes)
        {
            Console.WriteLine("Key: {0}, Value{1}", item.Key, item.Value);
        }
        路由匹配到的全部佔位符都保存在上面的routes裏面,
        若是一個佔位符被設置成 RouteParameter.Optional 那麼若是這個佔位符沒有提供的話,就不會添加到路由佔位符對象裏面
        defaults選項中也能夠包含routeTemplate中不存在的佔位符,這樣能夠保存一些額外的信息
    路由參數的獲取方式
        默認狀況下簡單的數據類型從URL中獲取,複雜的數據類型從請求體中獲取
  7. 基於路由屬性的控制器方法api

    上面咱們講的都是基於約定的路由,由於路由規則都是在配置文件中預先定義好的,能夠使用路由屬性更加靈活的配置請求
    路由配置文件中
        config.MapHttpAttributeRoutes(); 這段代碼就是屬性路由的配置,當方法沒有配置屬性路由,默認使用基於約定的路由
    使用例子
        [Route("yejiawei/{id}/haha")]
        [HttpPost]
        public int AllProducts(int id)
        {
            return id;
        }
        匹配的路由 yejiawei/3/haha 注意api已經不須要了
    能夠給控制器添加路由前綴
        [RoutePrefix("yejiawei")]
        那麼控制器的方法只須要簡寫成 
            [Route("{id}/haha")] 便可
        若是有的控制器方法的前綴不一致能夠使用~重寫
            [Route("~/api/{id}/haha")]
        還能夠添加路由約束
            [Route("{id:int}/haha")]
            支持的路由約束還有不少,自行查閱
        添加可選的路由參數
            [Route("{id:int?}/haha")] 控制器的方法要設置默認值
        添加路由參數默認值
            [Route("{id:int=2}/haha")]
  8. 路由參數跨域

    參數名稱必須相同,不區分大小寫
    get請求
        值類型參數從url中獲取
            public string GetDepartments(string str)
            {
                return str;
            }
            符合的api
            Departments?str=bbb
    post請求
        值類型參數默認從url中獲取
            public string PostDepartments(string str)
            {
                return str;
            }
            符合的api
            Departments?str=ccc
        引用類型的參數默認從請求體中獲取
            public class Demo
            {
                public string Name { get; set; }
                public string Sex { get; set; }
            }
            [Route("yejiawei/Departments")]
            public Demo PostDepartments(Demo obj)
            {
                return obj;
            }
            符合的api
                axios.post("yejiawei/Departments", {
                    Name: "yejiawei",
                    Sex: "女"
                }).then( (res) => { debugger }).catch( (err) => {console.log(err);})
            注意:應用類型的參數,只能存在一個,put方法的處理方式和post一致
    若是想修改默認的參數獲取規則,能夠使用 
        [FromUri] 可讓引用類型的屬性從uri中的參數獲取
            public class Demo
            {
                public string Name { get; set; }
                public string Sex { get; set; }
            }
            public Demo GetDepartments([FromUri] Demo obj)
            {
                return obj;
            }
            匹配的api以下
            axiosInstance.get("api/Departments", {
                params: {
                    Name: "yejiawei",
                    Sex: "女"
                }
            })
        [FromBody] 可讓值類型從請求體中獲取
            [Route("yejiawei/BodyTest")]
            public string Post ([FromBody] string name)
            {
                return name;
            }
            匹配的api以下
            axiosInstance.post("yejiawei/BodyTest", "=yejiawei")
            若是你不想上面這種奇怪的寫法,能夠參考以下
                首先定義一個類
                    public class Test
                    {
                        public string Name { get; set; }
                    }
                控制器方法改爲以下
                    [Route("yejiawei/BodyTest")]
                    public string Post ([FromBody] Test name)
                    {
                        return name.Name;
                    }
                符合的api寫法
                    axiosInstance.post("yejiawei/BodyTest", { name: "yejiawei" })
            注意:
                [FromBody] 只能使用於一個參數
  9. webapi跨域解決緩存

    使用開局介紹的配置只能簡單的解決get,post不帶請求體這類的跨域請求,若是想完全的跨域,請看下面
    第一步,安裝 Install-Package Microsoft.AspNet.WebApi.Cors
    第二步,在WebApiConfig.cs中添加 
        config.EnableCors();
    第三步,在會被跨域的控制器上添加 
        [EnableCors(origins: "http://mywebclient.azurewebsites.net", headers: "*", methods: "*")]
        origins對應前端發請求的地址
    第四步,註釋掉本文開頭所說配置中的
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="*" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
        幹掉上面這三句句話,此時你的跨域配置已經ok了
    注:
        [EnableCors] 能夠單獨給一個控制器方法使用
        [DisableCors] 能夠給一個單獨的控制器方法禁用跨域
        開啓全局跨域
            讓任何一個控制器都支持跨域,只須要將上面的第二步改爲
            var cors = new EnableCorsAttribute("http://localhost:3000", "*", "*");
            config.EnableCors(cors);
        設置規則的順序
            Action > Controller > Global
        origins參數詳解
            origins支持一逗號分隔,寫多個跨域地址,也能夠支持通配符 * 
        methods參數
            methods參數用來指定具體的http方法,能夠以逗號分隔寫多個,也支持通配符
        headers參數不須要手動配置,寫成通配符便可
  10. 請求和響應的數據格式化app

    MIME類型指定了數據的基本格式
    在HTTP請求中 
        Accept頭指定了客戶端指望獲得的數據格式
        Content-Type指定了請求發送的數據格式
    Web Api根據Content-Type頭指定的格式類型,將請求的數據轉化成對應的CLR對象,同時根據Accept頭將響應的結果序列化成對應的數據格式,Web Api 內置支持JSON, XML, BSON, form-urlencoded 序列化器,這就是說這幾種格式的數據Web Api會自動處理
    因此僅僅改變Content-Type和Accept就能夠方便的序列化請求或者響應的數據
    媒體類型格式序列化器
        webapi可以自動識別而且處理JSON和XML數據格式,是由於Web API包含如下的內置媒體格式化器
        媒體格式化器類 
            JsonMediaTypeFormatter 處理的MIME類型 application/json, text/json 用來處理JSON格式化
            XmlMediaTypeFormatter  處理的MIME類型 application/xml, text/json 用來處理XML格式化
            FormUrlEncodedMediaTypeFormatter 處理的MIME類型 application/x-www-form-urlencoded 處理HTML表達URL編碼的數據
            JQueryMvcFormUrlEncodedFormatter 處理的MIME類型 application/x-www-form-urlencoded 處理模型綁定的HTML表單URL編碼的數據
        訪問全部的媒體格式化器
            [Route("yejiawei/MIMEType")]
            public IEnumerable<string> Get()
            {
                IList<string> formatters = new List<string>();
                foreach(var item in GlobalConfiguration.Configuration.Formatters)
                {
                    formatters.Add(item.ToString());
                }
                return formatters.AsEnumerable<string>();
            }  
        單獨訪問指定的媒體格式化器
            GlobalConfiguration.Configuration.Formatters.JsonFormatter.GetType().FullName
            GlobalConfiguration.Configuration.Formatters.FormUrlEncodedFormatter.GetType().FullName
    webapi 處理Json格式的數據默認使用 PascalCase, 能夠設置成支持camelCase的
        在WebApiConfig.cs中添加以下代碼
        config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
  11. WebApi過濾器

    過濾器能夠在控制器方法調用以前和以後執行
    簡單的一個用法
        先建立一個實現ActionFilterAttribute的類
            public class LogAttribute : ActionFilterAttribute
            {
                public LogAttribute()
                {
                }
                public override void OnActionExecuting(HttpActionContext actionContext)
                {
                    Trace.WriteLine(string.Format("Action Method {0} executing at {1}", actionContext.ActionDescriptor.ActionName, DateTime.Now.ToShortDateString()), "Web API Logs");
                }
                public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
                {
                    Trace.WriteLine(string.Format("Action Method {0} executed at {1}", actionExecutedContext.ActionContext.ActionDescriptor.ActionName, DateTime.Now.ToShortDateString()), "Web API Logs");
                }
            }
        而後在控制器類的頭部添加屬性 [Log] 便可,當控制器調用方法時,OnActionExecuting會執行,當方法調用完畢之後OnActionExecuted會執行
  12. WebApi使用注意點

    在寫post,put請求以前,要先使用 ModelState.IsValid 判斷一下,傳進來的引用類型參數是否包含有效值
相關文章
相關標籤/搜索