asp.net mvc 之 asp.net mvc 4.0 新特性之 Web API: 开发一个 CRUD 的 Demo,服务端用 Web API,并使其支持 jsonp 协议,客户端用 jQuery


介绍

asp.net mvc 之 asp.net mvc 4.0 新特性之 Web API


  • 开发一个 CRUD 的 Demo,服务端用 Web API,并使其支持 jsonp 协议,客户端用 jQuery


示例

1、自定义一个 JsonMediaTypeFormatter,以支持 jsonp 协议

MyJsonFormatter.cs


/*  * 自定义一个 JsonMediaTypeFormatter,以支持 jsonp 协议  */  using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Threading.Tasks; using System.Web;  namespace MVC40.Controllers {     public class MyJsonFormatter : JsonMediaTypeFormatter     {         // jsonp 回调的函数名称         private string JsonpCallbackFunction;          public MyJsonFormatter()         {          }          public override bool CanWriteType(Type type)         {             return true;         }          // 每个请求都先来这里         public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType)         {             var formatter = new MyJsonFormatter()             {                 JsonpCallbackFunction = GetJsonCallbackFunction(request)             };              // 增加一个转换器,以便枚举值与枚举名间的转换             formatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());              // 增加一个转换器,以方便时间格式的序列化和饭序列化             var dateTimeConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter();             dateTimeConverter.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";             formatter.SerializerSettings.Converters.Add(dateTimeConverter);              // 排版返回的 json 数据,使其具有缩进格式,以方便裸眼查看             formatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;              return formatter;         }          // 序列化的实现         public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)         {             if (string.IsNullOrEmpty(JsonpCallbackFunction))                 return base.WriteToStreamAsync(type, value, stream, content, transportContext);              StreamWriter writer = null;              try             {                 writer = new StreamWriter(stream);                 writer.Write(JsonpCallbackFunction + "(");                 writer.Flush();             }             catch (Exception ex)             {                 try                 {                     if (writer != null)                         writer.Dispose();                 }                 catch { }                  var tcs = new TaskCompletionSource<object>();                 tcs.SetException(ex);                 return tcs.Task;             }              return base.WriteToStreamAsync(type, value, stream, content, transportContext)                        .ContinueWith(innerTask =>                             {                                 if (innerTask.Status == TaskStatus.RanToCompletion)                                 {                                     writer.Write(")");                                     writer.Flush();                                 }                              }, TaskContinuationOptions.ExecuteSynchronously)                         .ContinueWith(innerTask =>                             {                                 writer.Dispose();                                 return innerTask;                              }, TaskContinuationOptions.ExecuteSynchronously)                         .Unwrap();         }          // 从请求 url 中获取其参数 callback 的值         private string GetJsonCallbackFunction(HttpRequestMessage request)         {             if (request.Method != HttpMethod.Get)                 return null;              var query = HttpUtility.ParseQueryString(request.RequestUri.Query);             var queryVal = query["callback"];              if (string.IsNullOrEmpty(queryVal))                 return null;              return queryVal;         }     } }


2、在 Global 中做的一些配置

Global.asax.cs


using MVC40.Controllers; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing;  namespace MVC40 {     public class WebApiApplication : System.Web.HttpApplication     {         protected void Application_Start()         {             AreaRegistration.RegisterAllAreas();              WebApiConfig.Register(GlobalConfiguration.Configuration);             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);             RouteConfig.RegisterRoutes(RouteTable.Routes);             BundleConfig.RegisterBundles(BundleTable.Bundles);                            // 添加一个转换器 IsoDateTimeConverter,其用于日期数据的序列化和反序列化             var dateTimeConverter = new IsoDateTimeConverter();             dateTimeConverter.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";             JsonSerializerSettings serializerSettings = new JsonSerializerSettings();             serializerSettings.Converters.Add(dateTimeConverter);              // 清除全部 Formatter(默认有 4 个,分别是:JsonMediaTypeFormatter, XmlMediaTypeFormatter, FormUrlEncodedMediaTypeFormatter, JQueryMvcFormUrlEncodedFormatter)             // GlobalConfiguration.Configuration.Formatters.Clear();              // 如果请求 header 中有 accept: text/html 则返回这个新建的 JsonMediaTypeFormatter 数据             var jsonFormatter = new JsonMediaTypeFormatter();             jsonFormatter.SerializerSettings = serializerSettings;             // jsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));              jsonFormatter.MediaTypeMappings.Add(new RequestHeaderMapping("accept", "text/html", StringComparison.InvariantCultureIgnoreCase, true, new MediaTypeHeaderValue("text/html")));             GlobalConfiguration.Configuration.Formatters.Insert(0, jsonFormatter);               // 请求 url 中如果带有参数 xml=true,则返回 xml 数据             GlobalConfiguration.Configuration.Formatters.XmlFormatter.MediaTypeMappings.Add(new QueryStringMapping("xml", "true", "application/xml"));               // 请求 url 中如果带有参数 jsonp=true,则返回支持 jsonp 协议的数据(具体实现参见 MyJsonFormatter.cs)             MyJsonFormatter formatter = new MyJsonFormatter();             formatter.MediaTypeMappings.Add(new QueryStringMapping("jsonp", "true", "application/javascript"));             GlobalConfiguration.Configuration.Formatters.Add(formatter);         }     } }


关于项目模版生成的代码的简短说明


<p>     项目模板更新了,原来堆在 Global.asax.cs 中的配置都分出去了     <br />     在 App_Start 文件夹里自动生成的 BundleConfig.cs 用于对多个 css 或 js 做打包和压缩     <br />     在 App_Start 文件夹里自动生成的 FilterConfig.cs 用于配置全局的 Action Filter     <br />     在 App_Start 文件夹里自动生成的 RouteConfig.cs 用于配置路由     <br />     在 App_Start 文件夹里自动生成的 WebApiConfig.cs 用于为 web api 配置路由 </p>


web api 的路由配置

WebApiConfig.cs


/*  * web api 的路由配置  */  using System; using System.Collections.Generic; using System.Linq; using System.Web.Http;  namespace MVC40 {     public static class WebApiConfig     {         public static void Register(HttpConfiguration config)         {             // 此配置为默认生成的配置             config.Routes.MapHttpRoute(                 name: "DefaultApi",                 routeTemplate: "api/{controller}/{id}",                 defaults: new { id = RouteParameter.Optional }             );               // 由于默认为 web api 生成的路由配置,无 action 配置,所以只能通过 http 方法来匹配 action             // 如果需要带 action 的路由则使用以下配置即可             /*             routes.MapHttpRoute(                 name: "ActionApi",                 routeTemplate: "api/{controller}/{action}/{id}",                 defaults: new { id = RouteParameter.Optional }             );             */         }     } }


3、提供 Web API 服务的 Controller(数据层用 Entity Framework 5.0 来实现)

ProductsController.cs


/*  * ASP.NET Web API  *   * c - POST - 创建  * r - GET - 读取  * u - PUT - 更新  * d - DELETE - 删除  *   * 注:win8 的 iis 默认不会安装 asp.net,需要在“程序和功能”中手动添加  */  using MVC40.Models; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http;  namespace MVC40.Controllers {     /*      * Web API 的 Controller 要从 ApiController 继承      *       * 默认:http get 找 controller 的 get(), http post 找 controller 的 post(), http put 找 controller 的 put(), http delete 找 controller 的 delete()      */     public class ProductsController : ApiController     {         // 获取全部 Product 数据:get http://localhost:17612/api/products         public IEnumerable<Product> Get()         {             NorthwindEntities db = new NorthwindEntities();              var products = from p in db.Products                            orderby p.ProductID descending                            select p;              return products.ToList();         }          // 根据 ProductId 获取指定的 Product 数据:get http://localhost:17612/api/products/3         public Product Get(int id)         {             NorthwindEntities db = new NorthwindEntities();              var product = db.Products.SingleOrDefault(p => p.ProductID == id);              return product;         }          // 新建 Product:post 一个 product 到 http://localhost:17612/api/products         public void Post(Product product)         {             NorthwindEntities db = new NorthwindEntities();              db.Products.Add(product);              db.SaveChanges();          }          // 更新 Product:put 一个 product 到 http://localhost:17612/api/products         public void Put(Product product)         {             NorthwindEntities db = new NorthwindEntities();              db.Products.Attach(product);             var entry = db.Entry(product);             entry.State = EntityState.Modified;              db.SaveChanges();         }          // 根据 ProductId 删除指定的 Product 数据:delete http://localhost:17612/api/products/3         public void Delete(int id)         {             NorthwindEntities db = new NorthwindEntities();              var product = db.Products.SingleOrDefault(p => p.ProductID == id);             db.Products.Remove(product);              db.SaveChanges();         }     } }


4、调用 Web API 的客户端(用 jQuery 实现)

CRUDDemo.cshtml


@{     Layout = null; }  <!DOCTYPE html>  <html> <head>     <title>演示如何通过 jQuery 调用 asp.net web api 做 crud 操作</title>     <script src="../Scripts/jquery-1.7.1.js" type="text/javascript"></script> </head> <body>     <table id="tblProducts" border="1">         <tr>             <th>Product Id</th>             <th>Product Name</th>             <th>Unit Price</th>             <th>Actions</th>         </tr>         <tr>             <td>                 <input type="text" id="txtProductId" size="5" disabled /></td>             <td>                 <input type="text" id="txtProductName" /></td>             <td>                 <input type="text" id="txtUnitPrice" /></td>             <td>                 <input type="button" name="btnInsert" value="Insert" /></td>         </tr>     </table>      <script type="text/javascript">         $(document).ready(function () {             loadProductsAsync();         });          // 异步获取全部 Product 数据         function loadProductsAsync() {             $.getJSON("/api/products?jsonp=true&callback=?", loadProductsCompleted).error(function () { alert('error') });         }          // 显示获取到的 Product 数据         function loadProductsCompleted(data) {             $("#tblProducts").find("tr:gt(1)").remove();             $.each(data, function (key, val) {                 var tableRow = '<tr>' +                                 '<td>' + val.ProductID + '</td>' +                                 '<td><input type="text" value="' + val.ProductName + '"/></td>' +                                 '<td><input type="text" value="' + val.UnitPrice + '"/></td>' +                                 '<td><input type="button" name="btnUpdate" value="Update" />  <input type="button" name="btnDelete" value="Delete" /></td>' +                 '</tr>';                 $('#tblProducts').append(tableRow);             });              $("input[name='btnInsert']").click(onInsert);             $("input[name='btnUpdate']").click(onUpdate);             $("input[name='btnDelete']").click(onDelete);         }           // 新增 Product 数据         function onInsert(evt) {             var productName = $("#txtProductName").val();             var unitPrice = $("#txtUnitPrice").val();              // 构建需要 post 的数据,即需要添加的数据             var data = '{"ProductName":"' + productName + '","UnitPrice":' + unitPrice + '}';              $.ajax({                 type: 'POST',                 url: '/api/products/',                 data: data,                 contentType: "application/json; charset=utf-8",                 dataType: 'json',                 success: function (results) {                     $("#txtProductName").val('');                     $("#txtUnitPrice").val('');                      alert('Product Added');                      loadProductsAsync();                 }             })         }           // 更新 Product 数据         function onUpdate(evt) {             var productId = $(this).parent().parent().children().get(0).innerHTML;              var cell = $(this).parent().parent().children().get(1);             var productName = $(cell).find('input').val();              cell = $(this).parent().parent().children().get(2);             var unitPrice = $(cell).find('input').val();              // 构建需要 put 的数据,即需要更新的数据             var data = '{"ProductID":"' + productId + '","ProductName":"' + productName + '","UnitPrice":' + unitPrice + '}';                        $.ajax({                 type: 'PUT',                 url: '/api/products/',                 data: data,                 contentType: "application/json; charset=utf-8",                 dataType: 'json',                 success: function (results) {                     alert('Product Updated');                 }             })         }           // 删除指定的 Product 数据         function onDelete(evt) {             var productId = $(this).parent().parent().children().get(0).innerHTML;              $.ajax({                 type: 'DELETE',                 url: '/api/products/' + productId,                 contentType: "application/json; charset=utf-8",                 dataType: 'json',                 success: function (results) {                     alert('Product Deleted');                      loadProductsAsync();                 }             })          }     </script> </body> </html>



OK