返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API

简介: 原文:返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API[索引页][源码下载] 返璞归真 asp.net mvc (10) - asp.net mvc 4.
原文: 返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API

[索引页]
[源码下载]


返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API



作者:webabcd


介绍
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
[源码下载]

目录
相关文章
|
8天前
|
XML JSON API
Understanding RESTful API and Web Services: Key Differences and Use Cases
在现代软件开发中,RESTful API和Web服务均用于实现系统间通信,但各有特点。RESTful API遵循REST原则,主要使用HTTP/HTTPS协议,数据格式多为JSON或XML,适用于无状态通信;而Web服务包括SOAP和REST,常用于基于网络的API,采用标准化方法如WSDL或OpenAPI。理解两者区别有助于选择适合应用需求的解决方案,构建高效、可扩展的应用程序。
|
12天前
|
机器学习/深度学习 开发框架 API
Python 高级编程与实战:深入理解 Web 开发与 API 设计
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧以及数据科学和机器学习。本文将深入探讨 Python 在 Web 开发和 API 设计中的应用,并通过实战项目帮助你掌握这些技术。
|
2月前
|
开发框架 数据可视化 .NET
.NET 中管理 Web API 文档的两种方式
.NET 中管理 Web API 文档的两种方式
60 14
|
2月前
|
人工智能 前端开发 API
Gemini Coder:基于 Google Gemini API 的开源 Web 应用生成工具,支持实时编辑和预览
Gemini Coder 是一款基于 Google Gemini API 的 AI 应用生成工具,支持通过文本描述快速生成代码,并提供实时代码编辑和预览功能,简化开发流程。
173 38
Gemini Coder:基于 Google Gemini API 的开源 Web 应用生成工具,支持实时编辑和预览
|
3月前
|
运维 前端开发 C#
一套以用户体验出发的.NET8 Web开源框架
一套以用户体验出发的.NET8 Web开源框架
一套以用户体验出发的.NET8 Web开源框架
|
3月前
|
Kubernetes 安全 Devops
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
110 10
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
|
3月前
|
开发框架 .NET PHP
ASP.NET Web Pages - 添加 Razor 代码
ASP.NET Web Pages 使用 Razor 标记添加服务器端代码,支持 C# 和 Visual Basic。Razor 语法简洁易学,类似于 ASP 和 PHP。例如,在网页中加入 `@DateTime.Now` 可以实时显示当前时间。
|
3月前
|
开发框架 前端开发 JavaScript
ASP.NET Web Pages - 教程
ASP.NET Web Pages 是一种用于创建动态网页的开发模式,采用HTML、CSS、JavaScript 和服务器脚本。本教程聚焦于Web Pages,介绍如何使用Razor语法结合服务器端代码与前端技术,以及利用WebMatrix工具进行开发。适合初学者入门ASP.NET。
|
.NET API 数据格式
ASP.NET WebAPi之断点续传下载(上)
前言 之前一直感觉断点续传比较神秘,于是想去一探究竟,不知从何入手,以为就写写逻辑就行,结果搜索一番,还得了解相关http协议知识,又花了许久功夫去看http协议中有关断点续传知识,有时候发觉东西只有当你用到再去看相关内容时才会掌握的更加牢固,理解的更加透彻吧,下面我们首先来补补关于http协议中断点续传的知识。
1229 0
|
.NET 数据格式
ASP.NET WebAPi之断点续传下载(中)
前言 前情回顾:上一篇我们遗留了两个问题,一个是未完全实现断点续传,另外则是在响应时是返回StreamContent还是PushStreamContent呢?这一节我们重点来解决这两个问题,同时就在此过程中需要注意的地方一并指出,若有错误之处,请指出。
1216 0

热门文章

最新文章