net core体系-web应用程序-4asp.net core2.0 项目实战(1)-13基于OnActionExecuting全局过滤器,页面操作权限过滤控制到按钮级

1.权限管理

  权限管理的基本定义:百度百科

  基于《Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员、后台管理员同时登录》我们做过了登录认证,登录是权限的最基础的认证,没有登录就没有接下来的各种操作权限管理,以及数据权限管理(暂不探讨),这里我们把登录当作全局权限,进入系统后再根据不同的角色或者人员,固定基本功能的展示,当不同的角色要对功能操作时,就需要验证操作权限,如:查看/添加/修改/删除,也就是我们常说的控制到按钮级。下面让我们一步一步来操作实现一下,本篇提供一种权限过滤思路,欢迎讨论指正,全局过滤代码基类已经实现,相关控制页面还在紧急编码中,时间少任务重,希望大家多体谅。

内容略长:请耐心浏览。

2.约定大于配置

  约定优于配置,也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。与之对应的就是mvc下控制器和视图的关系。

  本质是说,开发人员仅需规定应用中不符约定的部分。例如,如果模型中有个名为Sale的类,那么数据库中对应的表就会默认命名为sales。只有在偏离这一约定时,例如将该表命名为”products_sold”,才需写有关这个名字的配置。

  为了方便项目快速构建,数据库我们这里先使用dtcms 5.0的数据库相关表navigation。EF Core生成Model备用。

a)  首先约定后台Controller和Action命名约定,以及属性Attribute类定义

  ##菜单约定##

  1.nav_name尽量使用controller

  2.所有英文小写

  3.最后一级url不能为空

  ##方法定义约定##

  1.属性全nav_name,action_type

  2.属性只有nav_name,判断Action和参数是否为空

  3.属性只有action_type,控制器名做nav_name

  4.根据控制器+Action判断

  5.不是标准方法必须加属性nav_name

  6.控制器标准,保存Action方法不标准,需要传标准参数

b)   定义操作枚举Enum

using System;
using System.Collections.Generic;
using System.Text;

namespace NC.Common
{
    public class JHEnums
    {

        /// <summary>
        /// 统一管理操作枚举
        /// </summary>
        public enum ActionEnum
        {
            /// <summary>
            /// 所有
            /// </summary>
            All,
            /// <summary>
            /// 显示
            /// </summary>
            Show,
            /// <summary>
            /// 查看
            /// </summary>
            View,
            /// <summary>
            /// 添加
            /// </summary>
            Add,
            /// <summary>
            /// 修改
            /// </summary>
            Edit,
            /// <summary>
            /// 删除
            /// </summary>
            Delete,
            /// <summary>
            /// 审核
            /// </summary>
            Audit,
            /// <summary>
            /// 回复
            /// </summary>
            Reply,
            /// <summary>
            /// 确认
            /// </summary>
            Confirm,
            /// <summary>
            /// 取消
            /// </summary>
            Cancel,
            /// <summary>
            /// 作废
            /// </summary>
            Invalid,
            /// <summary>
            /// 生成
            /// </summary>
            Build,
            /// <summary>
            /// 安装
            /// </summary>
            Instal,
            /// <summary>
            /// 卸载
            /// </summary>
            UnLoad,
            /// <summary>
            /// 登录
            /// </summary>
            Login,
            /// <summary>
            /// 备份
            /// </summary>
            Back,
            /// <summary>
            /// 还原
            /// </summary>
            Restore,
            /// <summary>
            /// 替换
            /// </summary>
            Replace,
            /// <summary>
            /// 复制
            /// </summary>
            Copy
        }
}

c)  获取操作权限

#region 操作权限菜单
        /// <summary>
        /// 获取操作权限
        /// </summary>
        /// <returns>Dictionary</returns>
        public static Dictionary<string, string> ActionType()
        {
            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic.Add("Show", "显示");
            dic.Add("View", "查看");
            dic.Add("Add", "添加");
            dic.Add("Edit", "修改");
            dic.Add("Delete", "删除");
            dic.Add("Audit", "审核");
            dic.Add("Reply", "回复");
            dic.Add("Confirm", "确认");
            dic.Add("Cancel", "取消");
            dic.Add("Invalid", "作废");
            dic.Add("Build", "生成");
            dic.Add("Instal", "安装");
            dic.Add("Unload", "卸载");
            dic.Add("Back", "备份");
            dic.Add("Restore", "还原");
            dic.Add("Replace", "替换");
            return dic;
        }
        #endregion

d)  Action属性类定义

using Microsoft.AspNetCore.Mvc.Filters;
using System;

namespace NC.Lib
{
    /// <summary>
    /// nav_name
    /// </summary>
    public class NavAttr : Attribute, IFilterMetadata
    {
        public NavAttr() { }
        public NavAttr(string navName, string actionType)
        {
            this.NavName = navName;
            this.ActionType = actionType;
        }
        public string NavName { set; get; }//菜单名称
        public string ActionType { set; get; }  //操作类型
    }
}

3. 全局过滤实现

3.1 首先定义一个基Controller

  定义好基AdminBase控制器后,所有的后台域Controller都继承此类

3.2 首先验证用户是否登录

Session相关参考3.5 Session操作

 

 /// <summary>
        /// 判断管理员是否已经登录
        /// </summary>
        public bool IsAdminLogin()
        {
            var bSession = HttpContext.Session.Get(AdminAuthorizeAttribute.AdminAuthenticationScheme);
            if (bSession == null)
            {
                return false;
            }
            siteAdminInfo = ByteConvertHelper.Bytes2Object<JhManager>(bSession);
            //如果Session为Null
            if (siteAdminInfo != null)
            {
                return true;
            }
            else
            {
                //检查Cookies
                var cookieAdmin = HttpContext.AuthenticateAsync(AdminAuthorizeAttribute.AdminAuthenticationScheme);
                cookieAdmin.Wait();
                var adminname = cookieAdmin.Result.Principal.Claims.FirstOrDefault(x => x.Type == "AdminName")?.Value;
                var adminpwd = cookieAdmin.Result.Principal.Claims.FirstOrDefault(x => x.Type == "AdminPwd")?.Value;

                if (adminname != "" && adminpwd != "")
                {
                    JhManager model = dblEf.JhManager.Where(m => m.UserName == adminname && m.Password == adminpwd).FirstOrDefault();
                    if (model != null)
                    {
                        HttpContext.Session.Set(AdminAuthorizeAttribute.AdminAuthenticationScheme, ByteConvertHelper.Object2Bytes(model));//存储session
                        bSession = HttpContext.Session.Get(AdminAuthorizeAttribute.AdminAuthenticationScheme);
                        siteAdminInfo = ByteConvertHelper.Bytes2Object<JhManager>(bSession);
                        return true;
                    }
                }
            }
            return false;
        }

3.3 OnActionExecuting重载方法实现过滤

  首先验证登录,然后判断需要过滤的area,判断是否有跳过属性(SkipAdminAuthorizeAttribute,做登录的时候定义过),最后判断菜单和按钮的权限。

  这里需要注意的是,OnActionExecuting和AdminAuthorizeAttribute. OnAuthorization的执行顺序,有的网友博客看到的是OnActionExcuting先执行,我这里测试的是先验证属性OnAuthorization,再执行OnActionExecuting;可能附加条件不同,这里不做过多探讨,调试的时候大家可试试。

 

/// <summary>
        /// 创建过滤器:***全局过滤器*** 过滤除登录登出等操作权限验证
        /// </summary>
        /// <param name="context"></param>
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            //1.验证是否登录
            //2.验证菜单权限
            //3.验证按钮权限
            //在action执行之前

            //判断是否加有SkipAdmin标签
            var skipAuthorize = filterContext.ActionDescriptor.FilterDescriptors.Where(a => a.Filter is SkipAdminAuthorizeAttribute).Any();
            if (!skipAuthorize)
            {
                //是否系统管理文件夹里文件,Areas》ad_min
                var isPermission = false;
                //获取controller和action
                var route = filterContext.RouteData.Values;

                string strArea = route["area"].ToString();//获取区域的名字,ad_min区域下的都需要权限验证
                if (strArea != null && strArea.Equals("ad_min"))
                {
                    isPermission = true;
                }
                //需要验证权限
                if (isPermission)

                {
                    var currController = route["controller"].ToString();
                    var curraction = route["action"].ToString();
                    var exceptCtr = UtilConf.Configuration["Site:exceptCtr"].Replace(",", ",");//防止中文逗号
                    var exceptAction = UtilConf.Configuration["Site:exceptAction"].Replace(",", ",");//防止中文逗号
                    //判断是否有例外控制器或Action校验是否例外,跳过验证
                    if (!exceptCtr.Contains(currController.ToLower()) && !exceptAction.Contains(curraction.ToLower()))
                    {
                        //验证是否登录
                        if (!IsAdminLogin())
                        {
                            string msg = string.Format("未登录或登录超时,请重新登录!");
                            filterContext.Result = new RedirectResult("~/ad_min/login?msg=" + WebUtility.UrlEncode(msg));
                            return;
                        }
                        //验证菜单权限
                        //验证按钮权限
                        //自定义方法属性
                        try
                        {
                            //获取属性
                            NavAttr actionAttr = filterContext.ActionDescriptor.FilterDescriptors.Where(a => a.Filter is NavAttr).Select(a => a.Filter).FirstOrDefault() as NavAttr;
                            string strNavName = string.Empty;
                            string strActionType = string.Empty;
                            if (actionAttr == null)
                            {
                                actionAttr = filterContext.ActionDescriptor.FilterDescriptors.GetType().GetCustomAttributes<NavAttr>().FirstOrDefault() as NavAttr;
                            }
                            if (actionAttr != null)
                            {
                                strNavName = actionAttr.NavName;
                                strActionType = actionAttr.ActionType;
                            }
                            //获取参数,由于action在mvc中属于关键词,所以使用act当作操作方式参数
                            string paramAction = "";
                            //paramAction = Request.Query["action"].ToString();
                            if (string.IsNullOrEmpty(paramAction))
                            {
                                if (route["act"] != null)
                                {
                                    paramAction = route["act"].ToString();
                                }
                            }
                            if (siteAdminInfo.RoleType != 1)//超管拥有所有权限
                            {
                                if (!ChkPermission(siteAdminInfo.RoleId, currController, curraction, strNavName, strActionType, paramAction))
                                {
                                    TempData["Permission"] = "您没有管理该页面的权限,请联系管理员!";
                                    filterContext.Result = new RedirectResult("~/ad_min/Home/Index");
                                    return;
                                    //返回固定错误json
                                }
                                else
                                {
                                    TempData["Permission"] = null;
                                }
                            }
                        }
                        catch (System.Exception ex)
                        {
                            throw ex;
                        }
                    }
                }
            }
        }

  页面权限验证,首先获取到页面的Controller和Action以及Action上面是否包含相关属性NavAttr,校验数据库中是否包含对此属性的定义。

/// <summary>
        /// 判断页面
        /// </summary>
        /// <param name="role_id">角色id</param>
        /// <param name="currController">当前控制器</param>
        /// <param name="currAction">当前</param>
        /// <param name="navName">方法上的属性</param>
        /// <param name="actionType">操作类型</param>
        /// <param name="paramAction">当为操作方法是传递的参数</param>
        /// <returns>没有权限返回false</returns>
        public bool ChkPermission(int? role_id, string currController, string currAction, string navName, string actionType, string paramAction)
        {
            //1.未配置页面,在方法上加属性/ad_min/Settings/SysConfigSave
            //2.控制器+Action  /admin/sys_config/index,/admin/sys_config/add,/admin/sys_config/edit
            //3.先判断已配置页面/admin/settings/sys_config
            bool result = true;
            var url = HttpContext.Request.Path.Value;
            if (url.Contains("/ad_min/home/index"))//后台首页不验证
            {
                return result;
            }
            DataTable dt = chkPermission(role_id);
            var action_type = actionType;
            //属性不为空
            if (!string.IsNullOrEmpty(navName) && !string.IsNullOrEmpty(actionType))//属性全
            {
                DataRow[] dr = dt.Select("nav_name=‘" + navName + "‘ and action_type=‘" + action_type + "‘");
                result = (dr.Count() > 0);
            }
            else if (!string.IsNullOrEmpty(navName) && string.IsNullOrEmpty(actionType))//属性只有nav_name
            {
                action_type = getActionType(currAction, paramAction);
                DataRow[] dr = dt.Select("nav_name=‘" + navName + "‘ and action_type=‘" + action_type + "‘");
                result = (dr.Count() > 0);
            }
            else if (string.IsNullOrEmpty(currController) && !string.IsNullOrEmpty(actionType))//控制器名:nav_name,属性只有action_type
            {
                DataRow[] dr = dt.Select("nav_name=‘" + currController + "‘ and action_type=‘" + action_type + "‘");
                result = (dr.Count() > 0);
            }
            else
            {
                //约定大于配置
                //控制器名:nav_name
                //Action:action_type
                if (!string.IsNullOrEmpty(currController) && !string.IsNullOrEmpty(currAction))
                {
                    //控制器+action
                    if (currAction.ToLower() == "index")//首页为展示
                    {
                        currAction = "View";
                    }
                    DataRow[] dr = dt.Select("nav_name=‘" + currController + "‘ and (action_type=‘" + currAction + "‘)");
                    result = (dr.Count() > 0);

                }
                //属性全空,控制器+Action验证不通过,参数不空
                if (!result && !string.IsNullOrEmpty(currController) && !string.IsNullOrEmpty(paramAction))//(控制器)+参数判断
                {
                    //参数可为Edit,Add,Del...
                    DataRow[] dr = dt.Select("nav_name=‘" + currController + "‘ and action_type=‘" + paramAction + "‘");
                    result = (dr.Count() > 0);
                }
                if (!result)//控制器+Action验证未通过
                {
                    //配置页面处理
                    DataTable dtNav = GetNavCacheList("link_url=‘" + url + "‘");//根据菜单URL,从缓存中检索调用ID
                    if (dtNav.Rows.Count > 0)
                    {
                        DataRow drNav = dtNav.Rows[0];
                        string nav_name = drNav["name"].ToString();//nav_name
                        action_type = getActionType(currAction, paramAction);

                        DataRow[] dr = dt.Select("nav_name=‘" + nav_name + "‘ and action_type=‘" + action_type + "‘");
                        result = (dr.Count() > 0);
                    }
                }
            }

            return result;
        }
        /// <summary>
        /// 判断是否有权限
        /// </summary>
        private DataTable chkPermission(int? role_id)
        {
            DataTable dt = CacheHelper.Get("permisson" + role_id) as DataTable;
            if (dt == null)
            {
                string strSql = "SELECT mrv.nav_name,mrv.action_type FROM manager_role mr LEFT JOIN manager_role_value mrv ON mr.id=mrv.role_id WHERE [email protected]_id";
                DbParameters p = new DbParameters();
                p.Add("@role_id", role_id);
                dt = Dbl.JHCMS.CreateSqlDataTable(strSql, p);
                CacheHelper.Set("permisson" + role_id, dt);
            }
            return dt;
        }
        /// <summary>
        /// 1.验证action是否标准约定
        /// 2.根据action=‘‘参数获取操作类型
        /// </summary>
        private string getActionType(string currAction, string paramAction)
        {
            if (currAction.ToLower().Contains("index") || currAction.ToLower().Contains("list"))//首先判断是否首页/列表等展示
            {
                return "View";
            }
            if (currAction.ToLower().Contains("save"))//如果包含保存save关键字,默认返回add
            {
                return string.IsNullOrEmpty(paramAction) ? "Add" : paramAction;
            }
            else if (currAction.ToLower().Contains("edit") || currAction.ToLower().Contains("update"))
            {
                return string.IsNullOrEmpty(paramAction) ? "Edit" : paramAction;
            }
            else if (currAction.ToLower().Contains("del"))
            {
                return string.IsNullOrEmpty(paramAction) ? "Delete" : paramAction;
            }
            //判断Action
            if (!string.IsNullOrEmpty(currAction))
            {
                if (Utils.ActionType().ContainsKey(currAction))//首字母要大写,约定
                    return currAction;
            }
            return string.IsNullOrEmpty(paramAction) ? "View" : paramAction;
        }

3.4 Controller中的约定

  1.NavAttr属性全部定义

/// <summary>
        /// 更新字典排序
        /// </summary>
        [NavAttr(NavName = "sys_navigation", ActionType = "Edit")]
        public JsonResult UpdateNav(string id, string nav)
        {}

  2.NavAttr属性之定义NavName(对应数据库中的name)

[NavAttr(NavName = "sys_navigation"]
        public JsonResult UpdateNav_Edit(string id, string nav)
        {}

  3.未定义Action属性,必须传递一个参数以确定操作类型

//(控制器)+参数判断

    public class sys_navigationController : AdminBase
    {
        public JsonResult UpdateNav_Edit(string id, string nav)
        {}
}

3.5 Session相关操作

  Session使用需要先在startup.cs中进行配置注入,找到方法ConfigureServices注入Session

  Configure中启用

  在控制器中的操作,存储:

JhManager bUser = getUserInfoByNameAndPwd(AdminName, adminpwd, true);
HttpContext.Session.Set(AdminAuthorizeAttribute.AdminAuthenticationScheme, ByteConvertHelper.Object2Bytes(bUser));//存储session

  读取:

var bSession =
HttpContext.Session.Get(AdminAuthorizeAttribute.AdminAuthenticationScheme);
if (bSession == null)
            {
                return false;
            }
            bUser= ByteConvertHelper.Bytes2Object<JhManager>(bSession);

  ByteConvertHelper是byte转换帮助类

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace NC.Common
{
    /// <summary>
    /// byte转换操作类,主要用于Session存储
    /// </summary>
    public class ByteConvertHelper
    {
        /// <summary>
        /// 将对象转换为byte数组
        /// </summary>
        /// <param name="obj">被转换对象</param>
        /// <returns>转换后byte数组</returns>
        public static byte[] Object2Bytes(object obj)
        {
            byte[] serializedResult = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(obj));
            return serializedResult;
        }

        /// <summary>
        /// 将byte数组转换成对象
        /// </summary>
        /// <param name="buff">被转换byte数组</param>
        /// <returns>转换完成后的对象</returns>
        public static object Bytes2Object(byte[] buff)
        {
            return JsonConvert.DeserializeObject<object>(Encoding.UTF8.GetString(buff));
        }

        /// <summary>
        /// 将byte数组转换成对象
        /// </summary>
        /// <param name="buff">被转换byte数组</param>
        /// <returns>转换完成后的对象</returns>
        public static T Bytes2Object<T>(byte[] buff)
        {
            return JsonConvert.DeserializeObject<T>(Encoding.UTF8.GetString(buff));
        }
    }
}

4.总结

  实战项目还在一点点开发中,碰到很多坑点,时间也很有限。工作越来越忙,总是抽时间兼顾学习联系,很累。NET技术更新换代很快,公司里还在沿用比较老的技术,可能大多数公司都是这样,程序不得不学新技术,企业不得不用成熟的技术。

  虽然不知道会做到哪一步,碰到的问题积累的点,在这里先记录下来,备查。项目如果成型或能够运行起来看到效果,到时候开源出来。有时候毕竟代码片段或者写博的时候有些地方不容易连贯起来,现在让我们先一起学习吧。

原文地址:https://www.cnblogs.com/hmit/p/10769659.html

时间: 2024-10-03 14:33:34

net core体系-web应用程序-4asp.net core2.0 项目实战(1)-13基于OnActionExecuting全局过滤器,页面操作权限过滤控制到按钮级的相关文章

net core体系-web应用程序-4asp.net core2.0 项目实战(1)-3项目架构说明

本文目录1. 摘要2. 框架介绍 3. 权限管理之多一点说明4. 总结 1.  摘要 NCMVC角色权限管理框架是由最近练习Net Core时抽时间整理的系统,后续能不能发展成一个cms还要看朋友们是否有需要或时间是否充实.这里NCMVC主要还是给想要学习接触Asp.Net Core2.0的朋友们提供一些借鉴以及坑点提醒等,项目现在还有很多不足,以后会尽量完善一些.写这个系列也没有完全按照原来列好的目录正序来写,倒叙/插叙都有也是项目练习到了顺便记录下来仅此而已.中间有朋友留言说没有源码理解起来

net core体系-web应用程序-4asp.net core2.0 项目实战(1)-5项目数据库操作封装操作

本文目录1. 摘要2. Ado.Net数据库操作封装类 3. EF Core数据库操作4. 总结 1.  摘要 Asp.Net Core2.0下操作MSSQL数据库,这里介绍两种操作方式,一种是.NET Framework的ADO.NET<Ado.Net百科>,另一种就是Net Core2.0下的一种orm操作EF Core,由于本人习惯Ado.Net编程模式,EF Core涉猎不是很深,推荐网友连接,本文有不写的不到之处欢迎大家批评指正. 2.  Ado.Net数据库操作封装类 2.1配置文

net core体系-web应用程序-4asp.net core2.0 项目实战(1)-2项目说明和源码下载

本文目录1. 摘要2. Window下运行 3.linux下运行4. 开发记录5. 总结 1.概要 写<Asp.Net Core 2.0 项目实战>系列断断续续已经很长时间了,期间很多朋友要求开放源码,但是项目代码一直有很多问题,就算勉强开源出来大家看起来也比较费劲,运行起来也看不到实际的效果.在公司平时真的是太忙了,很少有时间去系统的整理一个框架,只能以学习这种形式边学边做.承诺大家开源出来此项目我做到了,虽然项目依然有很多问题,我接下来会再完善.功能暂时不会再增加了,暂时先做到权限管理,以

net core体系-web应用程序7asp.net core日志组件

日志介绍 Logging的使用 1. appsettings.json中Logging的介绍 Logging的配置信息是保存在appsettings.json配置文件中的.因为之前介绍配置文件的时候我们使用的是appsettings.Development.json这个文件,所以在介绍Logging的时候我们也用它吧. 通过上图可以看到Logging节点,下面有两个子节点IncludeScopes和LogLevel. IncludeScopes表示是否要使用日志作用域.在同一个作用域下的日志都会

Asp.Net Core 2.0 项目实战(6)Redis配置、封装帮助类RedisHelper及使用实例

本文目录 1. 摘要 2. Redis配置 3. RedisHelper 4.使用实例 5. 总结 1.  摘要 由于內存存取速度远高于磁盘读取的特性,为了程序效率提高性能,通常会把常用的不常变动的数据存储在系统内存中,提升数据读取的速度,在C#下常见的内存操作有微软自带的内存处理.分布式缓存Memcached以及Redis,这里我们介绍Redis分布式缓存,另外两种缓存处理参考<Asp.Net Core 2.0 项目实战(8)Core下缓存操作.序列化操作.JSON操作等Helper集合类>

【无私分享:ASP.NET CORE 项目实战(第七章)】文件操作 FileHelper

目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在程序设计中,我们很多情况下,会用到对文件的操作,在 上一个系列 中,我们有很多文件基本操作的示例,在Core中有一些改变,主要是我们常用的Server.MapPath()不存在了,不知道后续的版本会不会有,在这里,我们只能自己封装方法去实现.今天,我们就对一些基本的操作写了一个 FileHelper 类,供大家探讨.在此要感谢以为不愿意透漏姓名的大神的帮助:@YINYEJUN 获取文件的绝对路径  在以前的操作中,这个应

ASP.NET Core 2.1 : 十.升级现有Core2.0 项目到2.1

.NET Core 2.1 终于发布了, 赶紧升级一下. 一. 安装SDK 首先现在并安装 SDK(64-bit) 安装完毕后如果新建项目可以看到已经有2.1的选项了 二. 更新现有2.0项目到2.1 1. 修改 项目版本 右键点击解决方案管理器中的项目, 选择编辑项目名.csproj 将 <TargetFramework>netcoreapp2.0</TargetFramework> 中的2.0改为2.1,保存. 2. 将引用 Microsoft.AspNetCore.All 

Win10 兼容性 Visual studio web应用程序 ASP.NET 4.0 尚未在 Web 服务器上注册

我原先的win8系统,vs2012打开解决方案还不会报asp.net4.0未在web服务器上注册这个错误,更新win10后,vs2012总是报这个问题 后来在网上找到了答案,分享给需要的朋友,原因是win10跟vs2012不兼容,解决方法一:下载补丁KB3002339或者方法二:直接用vs2015. 这个是在这个博客看到的,http://blog.csdn.net/luozhuang/article/details/47810703 补丁下载官方地址是:http://www.microsoft.

ASP.NET MVC Web API 学习笔记---第一个Web API程序

http://www.cnblogs.com/qingyuan/archive/2012/10/12/2720824.html 1. Web API简单说明 近来很多大型的平台都公开了Web API.比如百度地图 Web API,做过地图相关的人都熟悉.公开服务这种方式可以使它易于与各种各样的设备和客户端平台集成功能,以及通过在浏览器中使用 JavaScript来创建更丰富的HTML体验.所以我相信Web API会越来越有它的用武之地. 说道Web API很多人都会想到Web服务,但是他们仍然有