• ASP.NET 4.5新特性WebAPI从入门到精通


    在新出的MVC4中,增加了WebAPI,用于提供REST风格的WebService,新生成的WebAPI项目和典型的MVC项目一样,包含主要的Models、Views、Controllers等文件夹和Global.asax文件。Views对于WebAPI来说没有太大的用途,Models中的Model主要用于保存Service和Client交互的对象,这些对象默认情况下会被转换为Json格式的数据迚行传输,Controllers中的Controller对应于WebService来说是一个Resource,用于提供服务。和普通的MVC一样,Global.asax用于配置路由规则。 
    环境准备 
         建议使用VS2012以上版本创建WebAPI,如果是使用VS2010,需要安装VS2010 SP1升级包,MVC4升级包,打开VS2012创建如下: 
    第一步:新建ASP.NET Web应用程序

    第二步:建议WebAPI

    新生成的WebAPI项目和典型的MVC项目一样,包含主要的Models,Views,Controllers等文件夹和Global.asax文件

    注意:再次强调Views对于WebAPI来说没有太大的用途,Models中的Model主要用于保存Service和Client交互的对象,这些对象默认情况下会被转换为Json格式的数据进行传输,Controllers中的Controller对应于WebService来说是一个Resource,用于提供服务。和普通的MVC一样,Global.asax用于配置路由规则

    (二)Models 
    和WCF中的数据契约形成鲜明对比的是,MVC WebAPI中的Model就是简单的POCO,没有任何别的东西,如,你可以创建如下的Model 

    public class UserModel 
    { 
            public int Id { get; set; } 
            public string UserName { get; set; } 
            public string PassWord { get; set; } 
    } 

    注意:Model必须提供public的属性,用于json或xml反序列化时的赋值 
    (三)Controllers 
    MVC WebAPI中的Controllers和普通MVC的Controllers类似,不过不再继承于Controller,而改为继承API的ApiController,一个Controller可以包含多个Action,这些Action响应请求的方法与Global中配置的路由规则有关,在后面结束Global时统一说明 
     
    (四)Global 
    默认情况下,模板自带了两个路由规则,分别对应于WebAPI和普通MVC的Web请求,默认的WebAPI路由规则如下 

                routes.MapHttpRoute( 
                     name: "DefaultApi", 
                     routeTemplate: "api/{controller}/{id}", 
                     defaults: new { id = RouteParameter.Optional } 
                 ); 

    可以看到,默认路由使用的固定的api作为Uri的先导,按照微软官方的说法,用于区分普通Web请求和WebService的请求路径: 
    可以看到,默认的路由规则只指向了Controller,没有指向具体的Action,因为默认情况下,对于Controller中的Action的匹配是和Action的方法名相关联的:具体来说,如果使用上面的路由规则,对应下面的Controller:

    复制代码
    public class UserController : ApiController 
        { 
            public  List<UserModel> allModeList = new List<UserModel>() {  
              new UserModel(){ Id=1,UserName="zhang", PassWord="123"}, 
              new UserModel(){ Id=2,UserName="lishi", PassWord="123456"}, 
              new UserModel(){ Id=3,UserName="wang", PassWord="1234567"} 
            }; 
            //Get  api/User/ 
            public IEnumerable<UserModel> GetAll() 
            { 
                return allModeList; 
            } 
            //Get api/User/1 
            public IEnumerable<UserModel> GetOne(int id) { 
                return allModeList.FindAll((m) => { return m.Id == id; }); 
            } 
            //POST api/User/ 
            public bool PostNew(UserModel user) 
            { 
                try 
                { 
                    allModeList.Add(user); 
                    return true; 
                } 
                catch { 
                    return false; 
                } 
            } 
            //Delete api/User/ 
            public int DeleteAll() 
            { 
                return allModeList.RemoveAll((mode) => { return true; }); 
            } 
            //Delete api/User/1 
            public int DeleteOne(int id) { 
                return allModeList.RemoveAll((m) => { return m.Id == id; }); 
            } 
            //Put api/User  
            public int PutOne(int id, UserModel user) 
            { 
                List<UserModel> upDataList = allModeList.FindAll((mode) => { return mode.Id == id; }); 
                foreach (var mode in upDataList) 
                { 
                    mode.PassWord = user.PassWord; 
                    mode.UserName = user.UserName; 
                } 
                return upDataList.Count; 
            } 
    } 
    复制代码

    则,会有下面的对应关系:

    URLHttpMethod对应的Action名
    /api/User GET GetALL
    /api/User/1 GET GetOne
    /api/User POST PostNew
    /api/User/1 DELETE DeleteOne
    /api/User DELETE DeleteALL
    /api/User PUT PutOne


      

    客户端JS调用

    复制代码
    function getAll() { 
            $.ajax({ 
                url: "api/User/", 
                type: 'GET', 
                success: function (data) { 
                    document.getElementById("modes").innerHTML = ""; 
                    $.each(data, function (key, val) { 
                        var str = val.UserName + ': ' + val.PassWord; 
                        $('<li/>', { html: str }).appendTo($('#modes')); 
                    }); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('Error: ' + err); 
            }); 
       } 
    function find() { 
            $.ajax({ 
                url: "api/User/1" , 
                type: 'GET', 
                success: function (data) { 
                    document.getElementById("modes").innerHTML = ""; 
                    $.each(data, function (key, val) { 
                        var str = val.UserName + ': ' + val.PassWord; 
                        $('<li/>', { html: str }).appendTo($('#modes')); 
                    }); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('Error: ' + err); 
            }); 
        } 
     
     
        function add() { 
     
            $.ajax({ 
                url: "api/User/", 
                type: "POST", 
                dataType: "json", 
                data: { "Id":4,"UserName": "admin", "PassWord": "666666"}, 
                success: function (data) { 
                    getAll(); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('Error: ' + err); 
            }); 
     
        } 
     
        function removeUser() { 
            $.ajax({ 
                url: "api/User/3", 
                type: 'DELETE', 
                success: function (data) { 
                    document.getElementById("modes").innerHTML = ""; 
                    getAll(); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('Error: ' + err); 
            }); 
        } 
     
        function removeAll() { 
            $.ajax({ 
                url: "api/User/", 
                type: 'DELETE', 
                success: function (data) { 
                    document.getElementById("modes").innerHTML = ""; 
                    getAll(); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('Error: ' + err); 
            }); 
        } 
        function udpate() { 
            $.ajax({ 
                url: "api/User/1", 
                type: 'PUT', 
                dataType: "json", 
                data: { Id: 1, "UserName": "admin", "PassWord": "666666" }, 
                success: function (data) { 
                    document.getElementById("modes").innerHTML = ""; 
                    getAll(); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('Error: ' + err); 
            }); 
        } 
    复制代码

    这样就实现了最基本的CRUD操作。 
    扩展需求 
    问题1:我想按照用户名称(UserName)进行查询,怎么办? 
    办法:第一步:在UserController类中加一个方法名称叫:GetUserByName,如下所示: 

    public UserModel GetUserByName(string userName) { 
                return allModeList.Find((m) => { return m.UserName.Equals(userName); }); 
    } 

    第二步:在客户端index.cshtml中调用 

    复制代码
    function getUserByName() { 
            $.ajax({ 
                url: "api/User/zhang", 
                type: 'GET', 
                success: function (data) { 
                    document.getElementById("modes").innerHTML = ""; 
                    var str = data.UserName + ': ' + data.PassWord; 
                    $('<li/>', { html: str }).appendTo($('#modes')); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('Error: ' + err); 
            }); 
       } 
    复制代码

    如果URL是: url: "api/User/zhang",将会报错:Bad Request 
    原因是他会自动调用我们的GetOne(int id) 这个方法,类型转换出错 
    解决办法: 
    改变URL为: url: "api/User/?userName=zhang", 
    问题2:我想按用户名称(UserName) 和用户密码(PassWord)一起来进行查询,怎么办? 
    解决办法 
    第一步:UserController类中,可以重载一个GetUserByName的方法,如下所示: 

    public UserModel GetUserByName(string userName) { 
                return allModeList.Find((m) => { return m.UserName.Equals(userName); }); 
            } 

    第二步:客户端调用:

    复制代码
    function getUserByName() { 
            $.ajax({ 
                url: "api/User/?userName=zhang&passWord=123", //这里尤其需要注意 
                type: 'GET', 
                success: function (data) { 
                    document.getElementById("modes").innerHTML = ""; 
                    var str = data.UserName + ': ' + data.PassWord; 
                    $('<li/>', { html: str }).appendTo($('#modes')); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('Error: ' + err); 
            }); 
       } 
    复制代码

    路由规则扩展 
    和普通的MVC一样,MVC WebAPI支持自定义的路由规则,如:在上面的操作中,路由规则使用 
    "api/{controller}/{id}" 
    则限定了使用GET方式利用URL来传值时,controller后面的接收参数名为id,但是在Controller中,如果GetOne方法的接收参数名为key,是不会被匹配的,这是只需要新增一个新的路由规则,或修改原先的路由规则为: 
    "api/{controller}/{key}",如下所示:

    config.Routes.MapHttpRoute( 
                    name: "DefaultApi", 
                    routeTemplate: "api/{controller}/{key}", 
                    defaults: new { key = RouteParameter.Optional } 
                ); 

    当然,可以对路由进行更深的扩展,如:扩展成和普通MVC一样的路由: 
    "api/{controller}/{action}/{id}" 
    这样,就要求同时使用Action和HTTP方法进行匹配当然,根据微软的说法,这种使用是不被推荐的,因为这不符合大家对WebService的一般认知: 
    使用Attribute声明HTTP方法

    复制代码
           [HttpGet] 
           public IEnumerable<TestUseMode> FindAll() 
           [HttpGet] 
           public IEnumerable<TestUseMode> FindByKey(string key) 
           [HttpPost] 
           public bool Add(TestUseMode mode) 
           [HttpDelete] 
           public int RemoveByKey(string key) 
           [HttpDelete] 
           public int RemoveAll() 
           [HttpPut] 
           public int UpdateByKey(string key, string value) 
           [NonAction]   
           public string GetPrivateData() 
    复制代码

    当然,我只列出了方法名,而不是这些方法真的没有方法体...方法体是不变的,NoAction表示这个方法是不接收请求的,即使以GET开头。如果感觉常规的GET,POST,DELETE,PUT不够用,还可以使用AcceptVerbs的方式来声明HTTP方法,如:

    复制代码
    [AcceptVerbs("MKCOL", "HEAD")] 
    public int UpdateByKey(string key, string value) 
    { 
                List<TestUseMode> upDataList = allModeList.FindAll((mode) => { if (mode.ModeKey == key) return true; return false; }); 
                foreach(var mode in upDataList) 
                { 
                    mode.ModeValue = value; 
                } 
                return upDataList.Count; 
    } 
    复制代码

    附:什么是REST风格? 参考:什么是REST风格 
    http://hi.baidu.com/yankaiwei/item/1f0b37dd922d53ef3cc2cb69 
    第二部分:综合示例:应用ASP.NET MVC4+WebAPI+FluentData开发Web应用 
    第一步:创建数据库 
    NorthWind数据库的Customers表

    复制代码
    Create DataBase NorthWind 
    Go 
    Use NorthWind 
    Go 
    CREATE TABLE [dbo].[Customers]( 
        [CustomerID] [nchar](5) NOT NULL, 
        [CompanyName] [nvarchar](40) NOT NULL, 
        [ContactName] [nvarchar](30) NULL, 
        [ContactTitle] [nvarchar](30) NULL, 
        [Address] [nvarchar](60) NULL, 
        [City] [nvarchar](15) NULL, 
        [Region] [nvarchar](15) NULL, 
        [PostalCode] [nvarchar](10) NULL, 
        [Country] [nvarchar](15) NULL, 
        [Phone] [nvarchar](24) NULL, 
        [Fax] [nvarchar](24) NULL, 
    CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED  
    ( 
        [CustomerID] ASC 
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
    ) ON [PRIMARY] 
     
    GO 
    复制代码

    第二步:创建 FluentData.Entity层,创建Customer实体类 

    复制代码
    namespace FluentData.Entity 
    { 
        public class Customer 
        { 
            public string CustomerID { get; set; } 
            public string CompanyName { get; set; } 
            public string ContactName { get; set; } 
            public string ContactTitle { get; set; } 
            public string Address { get; set; } 
            public string City { get; set; } 
            public string Region { get; set; } 
            public string PostalCode { get; set; } 
            public string Country { get; set; } 
            public string Phone { get; set; } 
            public string Fax { get; set; } 
        } 
    } 
    复制代码

    第三步:利用FluentData做数据的持久化 
    首先引入FluentData.cs (见附件) 
    其次:创建DBHelper类,代码如下:

    复制代码
    public class DBHelper 
        { 
            public static IDbContext Context() { 
                //return new DbContext().ConnectionString("server=127.0.0.1;uid=sa;pwd=sa;database=TestDB", new SqlServerProvider()); 
                return new DbContext().ConnectionStringName("connString", new SqlServerProvider()); 
            } 
        } 
    复制代码

    然后不要忘记修改ASP.NET MVC层所在的Web.config,加入数据库连结字符串:

      <connectionStrings> 
        <add name="connString" connectionString="server=127.0.0.1;database=Northwind;uid=sa;pwd=sa;"/> 
      </connectionStrings>

    第三步:创建 CustomerService数据持久化类,代码如下:

    复制代码
    public class CustomerService 
        { 
            private IDbContext context = DBHelper.Context(); 
            public Customer Select(string customerId){ 
     
                return context.Select<Customer>("*").From("Customers").Where("CustomerID=@0").Parameters(customerId) 
                       .QuerySingle(); 
                 
            } 
            public List<Customer> SelectAll() { 
                return context.Select<Customer>("*").From("Customers").QueryMany(); 
            } 
     
            public List<Customer> SelectAll(string sortExpression) { 
                if (String.IsNullOrEmpty(sortExpression)) return null; 
                return context.Select<Customer>("*").From("Customers").OrderBy(sortExpression).QueryMany(); 
            } 
     
            public List<Customer> SelectAll(int currentPageIndex,int maxRows, string sortExpression) 
            { 
                var select = context.Select<Customer>("*").From("Customers"); 
                if (maxRows > 0) { 
                    if (currentPageIndex == 0) currentPageIndex = 1; 
                    select.Paging(currentPageIndex, maxRows); 
                } 
                if (!string.IsNullOrEmpty(sortExpression)) { 
                    select.OrderBy(sortExpression); 
                } 
     
                return select.QueryMany(); 
            } 
     
            public int CountAll() { 
                return context.Sql("select count(*) from Customers").QuerySingle<int>(); 
            } 
     
            public int Insert(Customer customer) { 
                return context.Insert<Customer>("Customers", customer).Execute(); 
            } 
     
            public int Update(Customer customer) { 
                return context.Update<Customer>("Customers", customer).Where("CustomerID", customer.CustomerID).Execute(); 
            } 
     
            public int Delete(string customerId) { 
                return context.Delete("Customers").Where("CustomerID", customerId).Execute(); 
            } 
     
            public int Delete(Customer customer) 
            { 
                return this.Delete(customer.CustomerID); 
            } 
        } 
    复制代码

    第四步:Web API,创建CustomerController 
    注意要引用:FluentData.Entity及FluentData.DAL 程序集

    复制代码
    public class CustomerController : ApiController 
        { 
            private CustomerService customerService = new CustomerService(); 
            //Select All 
            public IEnumerable<Customer> Get() 
            { 
                return customerService.SelectAll(); 
            } 
     
            //Select By Id 
            public Customer Get(string id) 
            { 
                return customerService.Select(id); 
            } 
     
            //Insert 
            public void Post(Customer customer) 
            { 
                customerService.Insert(customer); 
            } 
     
     
            //Update 
            public void Put(string id, Customer obj) 
            { 
                customerService.Update(obj); 
            } 
     
            //Delete 
            public void Delete(string id) 
            { 
                customerService.Delete(id); 
            } 
        } 
    复制代码

    第五步:View层代码

    复制代码
    namespace MyWebApI.Controllers 
    { 
        public class HomeController : Controller 
        { 
            public ActionResult Index() 
            { 
                return View(); 
            } 
     
            public ActionResult Test() { 
                return View(); 
            } 
     
            public ActionResult CustomerManager() { 
                return View(); 
            } 
        } 
    } 
    复制代码

    然后创建View 

    复制代码
        <table id="customerTable" border="1" cellpadding="3"  style="700px"> 
            <tr> 
                <th>Customer ID</th> 
                <th>Company Name</th> 
                <th>Contact Name</th> 
                <th>Country</th> 
                <th>Actions</th> 
            </tr> 
            <tr> 
                <td><input type="text" id="txtCustomerId"  style="100px" size="5"/></td> 
                <td><input type="text" id="txtCompanyName"  style="150px" /></td> 
                <td><input type="text" id="txtContactName"  style="150px"/></td> 
                <td><input type="text" id="txtCountry"  style="150px"/></td> 
                <td><input type="button" name="btnInsert" value="Insert"  style="150px"/></td> 
            </tr> 
        </table> 
     
    <script type="text/javascript"> 
        $(function () { 
            $.getJSON("api/Customer", LoadCustomers); 
        }); 
     
        function LoadCustomers(data) { 
            $("#customerTable").find("tr:gt(1)").remove(); 
            $.each(data, function (key, val) { 
                var tableRow = '<tr>' + 
                                '<td>' + val.CustomerID + '</td>' + 
                                '<td><input type="text" value="' + val.CompanyName + '" /></td>' + 
                                '<td><input type="text" value="' + val.ContactName + '" /></td>' + 
                                '<td><input type="text" value="' + val.Country + '" /></td>' + 
                                '<td><input type="button" name="btnUpdate" value="修改" /> <input type="button" name="btnDelete" value="删除" /></td>' + 
                                '</tr>'; 
                $('#customerTable').append(tableRow); 
            }); 
     
            $("input[name='btnInsert']").click(OnInsert); 
            $("input[name='btnUpdate']").click(OnUpdate); 
            $("input[name='btnDelete']").click(OnDelete); 
        } 
     
        function OnInsert(evt) { 
            var customerId = $("#txtCustomerId").val(); 
            var companyName = $("#txtCompanyName").val(); 
            var contactName = $("#txtContactName").val(); 
            var country = $("#txtCountry").val(); 
            var data = '{"CustomerID":"' + customerId + '","CompanyName":"' + companyName + '","ContactName":"' + contactName + '","Country":"' + country + '"}'; 
     
            $.ajax({ 
                type: 'POST', 
                url: '/api/Customer/', 
                data: data, 
                contentType: "application/json; charset=utf-8", 
                dataType: 'json', 
                success: function (results) { 
                    $("#txtCustomerId").val(''); 
                    $("#txtCompanyName").val(''); 
                    $("#txtContactName").val(''); 
                    $("#txtCountry").val(''); 
                    $.getJSON("api/customers" + new Date().getTime(), LoadCustomers); 
                    alert('添加成功!'); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('添加失败,原因如下: ' + err); 
            }); 
        } 
        function OnUpdate(evt) { 
     
            var input; 
            var customerId = $(this).parent().parent().children().get(0).innerHTML; 
     
            input = $($(this).parent().parent().children().get(1)).find("input"); 
            //input.removeAttr("disabled"); 
            var companyName = input.val(); 
     
            input = $($(this).parent().parent().children().get(2)).find("input"); 
            //input.removeAttr("disabled"); 
            var contactName = input.val(); 
     
            input = $($(this).parent().parent().children().get(3)).find("input"); 
            //input.removeAttr("disabled"); 
            var country = input.val(); 
     
            var data = '{"CustomerID":"' + customerId + '","CompanyName":"' + companyName + '","ContactName":"' + contactName + '","Country":"' + country + '"}'; 
             
            $.ajax({ 
                type: 'PUT', 
                url: '/api/Customer/' + customerId, 
                data: data, 
                contentType: "application/json; charset=utf-8", 
                dataType: 'json', 
                success: function (results) { 
                    $.getJSON("api/Customer" + new Date().getTime(), LoadCustomers); 
                    alert('修改成功 !'); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('修改失败,原因如下: ' + err); 
            }); 
        } 
        function OnDelete(evt) { 
            var customerId = $(this).parent().parent().children().get(0).innerHTML; 
            //var data = '{"id":"' + customerId + '"}'; 
            //var row = $(this).parent().parent(); 
     
            $.ajax({ 
                type: 'DELETE', 
                url: '/api/Customer/' + customerId, 
                contentType: "application/json; charset=utf-8", 
                dataType: 'json', 
                success: function (results) { 
                    $.getJSON("api/Customer?"+new Date().getTime(), LoadCustomers); 
                    alert('成功删除!'); 
                } 
            }).fail( 
            function (xhr, textStatus, err) { 
                alert('删除失败,原因如下: ' + err); 
            }); 
     
        } 
    </script> 
    复制代码

    第三部分:Web API高级部分 
    在第一部分和大家一起学习了建立基本的WebAPI应用,第二部分写了一个综合示例,立刻就有人想到了一些问题:1.客户端和WebService/WebAPI之间文件传输2.客户端或者服务端的安全控制要解决这些问题,要了解一下WebAPI的基本工作方式。 
    (一)WebAPI中工作的Class 
    在MVC中大家都知道,获取Request和Response使用HttpRequest和HttpResponse两个类,在WebAPI中使用两外两个类:HttpRequestMessage 和HttpResponseMessage,分别用于封装Requset和Response。除了这两个类之外,还有一个常见的抽象 类:HttpMessageHandler,用于过滤和加工HttpRequestMessage和HttpResponseMessage 
     
    (二)解决第一个问题:客户端和WebService之间文件传输其 实第一个问题之所以被提出来应该是和客户端有关,如果客户端的请求是我们手写提交的,比如使用HttpClient封装的请求,则要传递文件之前,我们一 般会进行一次序列化,转化为二进制数组之类的,在网络上传输。这样的话,在Controller中的Action参数里,我们只需要接收这个二进制数组类 型的对象就可以了。但是如果客户端是Web Form呢,比如我们提交一个Form到指定的Controller的Action中,这个Action要接收什么类型的参数呢?或者我们问另外一个问题,如果我将Web Form提交到一个WebAPI的Action中 ,我要怎么去取出这个表单中的数据呢?其 实我们应该想到:我们的Action设置的参数之所以能够被赋值,是因为WebAPI的架构中在调用Action时将HTTP请求中的数据解析出来分别赋 值给Action中的参数,如果真是这样的话,我们只需要在Action中获取到HTTP请求,然后直接获取请求里面的数据,就能解决上面的问题。这 种想法是正确的,只不过,此时的HTTP请求已经不是最原始的HTTP Request,而是已经被转化成了HttpRequestMessage,在Action中,我们可以直接调用base.Requet来得到这个HttpRequestMessage实例,通过这个实例我们就可以随心所欲的取出HTTP请求中想要的数据 
     
    2.1从RequestMessage中获取普通表单数据 
    这里的普通表单是指不包含File的表单,也就是说表单的enctype值不是multipart/form-data,这时,表单的数据默认情况下是以Json来传递的如下页面

    复制代码
    <form name="form" action="~/api/File/Register" method="post"> 
        <input type="text" name="userName" /> 
        <br /> 
        <input type="text" name="passWord"  /> 
        <br />     
        <input type="submit" value="Submit" />  
    </form>
    复制代码

    ApiContoller:

    复制代码
      public class FileController : ApiController 
      { 
            [HttpPost] 
            public async Task  Register() 
            { 
                  HttpContent content = Request.Content; 
                 var jsonValue = await content.ReadAsStringAsync(); 
                Console.WriteLine(jsonValue); 
            } 
      }
    复制代码

    执行:userName字段输入admin,passWord字段输入123456 
    传递到服务器端输出的是:userName=admin&passWord=12345 
    2.2从RequestMessage中获取multipart表单数据将view页面改写为:

    复制代码
    <form name="form" action="~/api/File/SubmitFile" method="post" enctype="multipart/form-data" > 
        <input type="text" name="userName2"  /> 
        <br /> 
        <input type="text" name="passWord2" /> 
        <br /> 
        <input type="file" name="file" id="upFile" /> 
        <br /> 
        <input type="submit" value="Submit" /> 
    </form>
    复制代码

    ApiController代码:

    复制代码
            public async Task<string> SubmitFile() 
            { 
                // 检查是否是 multipart/form-data 
                if (!Request.Content.IsMimeMultipartContent("form-data")) 
                    throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); 
                var path = HttpContext.Current.Server.MapPath("~/File"); 
                // 设置上传目录 
                var provider = new MultipartFormDataStreamProvider(path); 
                // 接收数据,并保存文件 
                var bodyparts = await Request.Content.ReadAsMultipartAsync(provider); 
                var file = provider.FileData[0];//provider.FormData  
                string orfilename = file.Headers.ContentDisposition.FileName.TrimStart('"').TrimEnd('"'); 
                FileInfo fileinfo = new FileInfo(file.LocalFileName);   
                String ymd = DateTime.Now.ToString("yyyyMMdd", System.Globalization.DateTimeFormatInfo.InvariantInfo); 
                String newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", System.Globalization.DateTimeFormatInfo.InvariantInfo); 
                string fileExt = orfilename.Substring(orfilename.LastIndexOf('.')); 
                fileinfo.CopyTo(Path.Combine(path, newFileName + fileExt), true); 
                fileinfo.Delete();  
                string result = ""; 
                // 获取表单数据 
                result += "formData userName: " + bodyparts.FormData["userName1"]; 
                result += "<br />"; 
                // 获取文件数据 
                result += "fileData headers: " + bodyparts.FileData[0].Headers; // 上传文件相关的头信息 
                result += "<br />"; 
                result += "fileData localFileName: " + bodyparts.FileData[0].LocalFileName; // 文件在服务端的保存地址,需要的话自行 rename 或 move 
                return result; 
            } 
    复制代码

    还有一种简单的方式

    复制代码
    public string Post()  
             { 
                    HttpPostedFile file = HttpContext.Current.Request.Files[0]; 
                    string strPath = "D:\MyProjects\StudySolution\RestDemo\Upload\test2.rar" ; 
                    file.SaveAs(strPath); 
                    string result = "0"; 
                    return result; 
              } 
    复制代码

    注:上述的文件上传代码涉及到async、Task、await 这些关键字是 asp.net mvc 中异步编程的知识点,在这里暂不做过多解释,不了解的同学可以去先了解一下这块的内容,后期在我的系列主题文章中也还会有这块知识点的讲解,敬请关注! 
    解决第二个问题:客户端或者服务端的安全控制 
    WebAPI的工作方式:HTTP的请求最先是被传递到HOST中的,如果WebAPI是被寄宿在IIS上的,这个HOST就是IIS上,HOST是没有能力也没有必要进行请求的处理的,请求通过HOST被转发给了HttPServer此时已经进入WebAPI的处理加工范围,HttpServer是 System.Net.HTTP中的一个类,通过HttpServer,请求被封装成了WebAPI中的请求承载 类:HttpRequestMessage,这个封装后的请求可以经过一系列自定义的Handler来处理,这些handler串联成一个 pipeline,最后请求会被传递给HttpControlDispather,这个类通过对路由表的检索来确定请求将被转发到的具体的 Controller中的Action。 
       由此我们早就可以看出,想要解决第二个问题,可以直接在Handler PipeLine中进行,这种AOP风格的过滤器(拦截器)在REST的Webservice的安全验证中应用很广,一般大家比较乐于在HTTP头或者在 HTTP请求的URL中加上身份验证字段进行身份验证,下面举一个在Http头中添加身份验证信息的小例子: 
    3.1客户端客户端的customhandler用于将身份验证信息添加入报头

    复制代码
    class RequestCheckHandler : DelegatingHandler 
    { 
            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) 
            { 
                request.Headers.Add("keyword", "ibeifeng"); 
                return base.SendAsync(request, cancellationToken); 
            } 
    } 
    复制代码

    注:1.customhandler继承自DelegatingHandler类,上面已经说过,WebAPI的客户端和服务端被设计为相互对应的两套结构,所以不论是在客户端还是服务端,customhandler都是继承自DelegatingHandler类2.DelegatingHandler的sendAsync方法便是处理请求和接受请求时会被调用的方法,该方法返回值是HttPResponseMessage,接收的值为HttpRequestMessage,符合我们的一般认知3.方法的最后,调用base.SendAsync是将Request继续向该pipeline的其他customHandler传递,并获取其返回值。由于该方法不包含Response的处理逻辑,只需直接将上一个CustomHandler的返回值直接返回 
     
    客户端主程序: 

    复制代码
    static void Main(string[] args) 
    { 
               HttpClient client = new HttpClient(new RequestCheckHandler() { InnerHandler = new HttpClientHandler() }); 
                HttpResponseMessage response = client.GetAsync("http://localhost:47673/api/File/GetUserInfo?userName=admin").Result; 
                response.Content.ReadAsStringAsync().ContinueWith((str) => { Console.WriteLine(str.Result); }); 
    } 
    复制代码

    客户端的主程序创建了一个HttpClient,HttpClient可以接受一个参数,该参数就是CustomHandler,此处我们嵌入了我们定义的 RequestUpHandler,用于对Request报头进行嵌入身份验证码的处理,CustomHandler通过InnerHandler属性嵌 入其内置的下一个CustomHandler,此处,由于没有下一个CustomerHandler,我们直接嵌入HttpClientHandler用 于将HttpRequestMessage转化为HTTP 请求、将HTTP响应转化为HttpResponseMessage 
     
    3.2服务端服务端的customHandler用于解析HTTP报头中的身份认证码 

    复制代码
    public class SecurityHandler : DelegatingHandler 
    { 
       protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken  
          cancellationToken) 
        { 
                int matchHeaderCount = request.Headers.Count((item) => 
                { 
                    if ("keyword".Equals(item.Key)) 
                    { 
                        foreach (var str in item.Value) 
                        { 
                            if ("ibeifeng".Equals(str)) 
                            { 
                                return true; 
                            } 
                        } 
                    } 
                    return false; 
                }); 
                if (matchHeaderCount>0) 
                { 
                    return base.SendAsync(request, cancellationToken); 
                } 
                return Task.Factory.StartNew<HttpResponseMessage>(() => { return new HttpResponseMessage(HttpStatusCode.Forbidden); }); 
       } 
    } 
    复制代码

    另: 
    FileController中加一个测试方法:

    复制代码
            [HttpGet] 
            public string GetUserInfo(string userName) 
            { 
                if (userName == "admin") 
                { 
                    return "success"; 
                } 
                else { 
                    return "failed"; 
                }  
            }
    复制代码

    注:代码的处理逻辑很简单:如果身份验证码匹配成功,则通过base.SendAsync继续将请求向下传递,否则返回直接中断请求的传递,直接返回一个响应码为403的响应,指示没有权限。注意由于SendAsync的返回值需要封装在Task之中,所以需要使用Task.Factory.StartNew将返回值包含在Task中将customHandler注入到HOST中本例中WebAPI HOST在IIS上,所以我们只需将我们定义的CustomHandler在Application_Start中定义即可 

    protected void Application_Start() 
            { 
                //省略其他逻辑代码 
                GlobalConfiguration.Configuration.MessageHandlers.Add(new SecurityHandler ()); 
            } 

    由于WebAPI Host在IIS上,所以HttpServer和HttpControllerDispatcher不用我们手工处理 
    在加上上面的处理后,如果没有身份验证码的请求,会得到如下的响应 
     
     
    总结 
    1.使用WebAPI的目的 
      当你遇到以下这些情况的时候,就可以考虑使用Web API了。 
      a. 需要Web Service但是不需要SOAP 
      b. 需要在已有的WCF服务基础上建立non-soap-based http服务 
      c. 只想发布一些简单的Http服务,不想使用相对复杂的WCF配置 
      d. 发布的服务可能会被带宽受限的设备访问 
      e. 希望使用开源框架,关键时候可以自己调试或者自定义一下框架 
    2.使用WebAPI的几种方式 与注意事项 
    3.使用WebAPI实现文件上传 
    4.如何加强WebAPI的安全性 

  • 相关阅读:
    HTML初步学习7
    HTML初步学习6
    HTML初步学习5
    HTML初步学习4
    poj3449Geometric Shapes
    poj2074Line of Sight(直线相交)
    2014 Multi-University Training Contest 4
    poj3347Kadj Squares
    poj1556The Doors
    poj3608Bridge Across Islands(凸包间最小距离)
  • 原文地址:https://www.cnblogs.com/micenote/p/5063825.html
Copyright © 2020-2023  润新知