一、校验 — 表单不是你想提想提就能提

1.1 DataAnnotations(数据注解)

位于 System.ComponentModel.DataAnnotations 命名空间中的特性指定对数据模型中的各个字段的验证。这些特性用于定义常见的验证模式,例如范围检查和必填字段。而 DataAnnotations 特性使 MVC 能够提供客户端和服务器验证检查,使你无需进行额外的编码来控制数据的有效

通过为模型类增加数据描述的 DataAnnotations ,我们可以容易地为应用程序增加验证的功能。DataAnnotations 允许我们描述希望应用在模型属性上的验证规则,ASP.NET MVC 将会使用这些 DataAnnotations ,然后将适当的验证信息返回给用户。

在DataAnnotations为我们所提供的众多内置验证特性中,用的最多的其中的四个是:

(0)[DisplayName]:显示名 – 定义表单字段的提示名称

(1)[Required] :必须 – 表示这个属性是必须提供内容的字段

(2)[StringLength]:字符串长度 – 定义字符串类型的属性的最大长度

(3)[Range]:范围 – 为数字类型的属性提供最大值和最小值

(4)[RegularExpression]:正则表达式 – 指定动态数据中的数据字段值必须与指定的正则表达式匹配

1.2 使用DataAnnotations为Model进行校验

假设我们的Model中有一个UserInfo的实体,其定义如下:

1
2
3
4
5
6
public class UserInfo
    {
        public int Id { get; set; }
        public string UserName { get; set; }
        public int Age { get; set; }
    }

UserInfo的属性很简单,只有三个:Id,UserName和Age三个字段;现在我们可以为其增加验证特性,看看其为我们提供的强大的校验功能。

(1)非空验证

添加特性:

1
2
3
4
5
6
7
[Display(Name="用户名")]
[Required(ErrorMessage = "*姓名必填")]
public string UserName { get; set; }
 
[Display(Name = "年龄")]
[Required(ErrorMessage = "*年龄必填")]
public int Age { get; set; }

验证效果:

(2)字符串长度验证

添加特性:

1
2
3
4
[Display(Name="用户名")]
[Required(ErrorMessage = "*姓名必填")]
[StringLength(5, ErrorMessage = "*长度必须小于5")]
public string UserName { get; set; }

验证效果:

(3)范围验证

添加特性:

1
2
3
4
[Display(Name = "年龄")]
[Required(ErrorMessage = "*年龄必填")]
[Range(18, 120)]
public int Age { get; set; }

验证效果:

(4)正则表达式验证

添加特性:验证用户输入的是否是数字,正则表达式匹配

1
2
3
4
5
[Display(Name = "年龄")]
[Required(ErrorMessage = "*年龄必填")]
[Range(18, 120)]
[RegularExpression(@"^\d+$", ErrorMessage = "*请输入合法数字")]
public int Age { get; set; }

验证效果:

(5)浏览所生成的HTML代码

从上图可以看出,我们在浏览器端的校验都是通过为html标签设置自定义属性来实现的,我们在Model中为其添加的各种校验特性,都会在客户端生 成一个特定的属性,例如:data-val-length-max=“5”与data-val-length=”*长度必须小于5″对应 [StringLength(5, ErrorMessage = "*长度必须小于5")]。然后,通过jquery validate在客户端每次提交之前进行校验,如果校验匹配中有不符合规则的,则将message显示在一个特定的span标签 (class=”field-validation-valid”)内,并阻止此次表单提交操作。

1.3 使用DataAnnotations的注意事项

(1)首先,要确保需要进行校验的页面中引入了指定的几个js文件:

1
2
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

当然,jquery库的js文件也是必须的,而且在上面这两个js之前引入;

(2)在 Web.config 的appSettings中,已经默认支持了客户端验证(MVC3.0及更高版本中默认支持,MVC2.0则需要修改一下):

1
2
3
<!-- 是否启用全局客户端校验 -->
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />

PS:Unobtrusive Javascript有三层含义:

一是在HTML代码中不会随意的插入Javsscript代码,只在标签中加一些额外的属性值,然后被引用的脚本文件识别和处理;

二是通过脚本文件所增加的功能是一种渐进式的增强,当客户端不支持或禁用了Javsscript时网页所提供的功能仍然能够实现,只是用户体验会降低;

三是能够兼容不同的浏览器。

(3)在Action中如果要对客户端是否通过了校验进行验证,可以通过以下代码实现:

1
2
3
4
5
6
7
8
9
10
[HttpPost]
public ActionResult Add(UserInfo userInfo)
{
    if (ModelState.IsValid)
    {
         // To do fun
    }
 
    return RedirectToAction("Index");
}

如果通过校验,则ModelState的IsValid属性(bool类型)会变为true,反之则为false。

二、ASP.Net MVC下的两种AJAX方式

2.1 使用JQuery AJAX方式

首先,在ASP.Net MVC中使用此种方式跟普通的WebForm的开发方式是一致的,需要注意的是:Url地址不同->请求的是Controller下的 Action,例如在WebForm中请求的url通常是/Ajax/UserHandler.ashx,而在MVC中请求的url通常为:/User /GetAll。

例如,我们在一个View中添加一个按钮,用于使用AJAX获取一个服务器端的时间:

1
2
<h1>JQuery Ajax方式</h1>
<input id="btnJQuery" type="button" value="获取服务器时间" />

在Home控制器中增加一个用于返回时间的Action:

1
2
3
public ActionResult GetServerDate()
{return Content(DateTime.Now.ToString());
}

在View中增加一段JQuery代码,为btnJQuery按钮绑定一个Click事件:

1
2
3
4
5
6
7
8
9
$(function () {
        $("#btnJQuery").click(function () {
            $.post("/Home/GetServerDate", {}, function (data) {
                if (data != null) {
                    $("#spTime").html(data);
                }
            });
        });
});

这里通过JQuery AJAX发送一个异步的POST请求,获取服务器时间结果,并将其显示在span标签内:

至此,一个使用JQuery Ajax的MVC页面就完成了。但是,这仅是一个最简单的AJAX示例,在实际开发中往往比较复杂一点。

需要注意的是:

(1)如果你在JQuery AJAX中使用的是get方式的提交,那么在在使用Json返回JsonResult时注意要将第二个参数设置允许Get提交方式:return Json(“”,JsonRequestBehavior.AllowGet),否则你用get方式是无权执行要请求的Action方法的。

(2)在Ajax开发中要注意Ajax方法体内的参数设置正确,特别是参数名要和Action中的参数名保持一致;

(3)如果在Action中为其设置了[HttpPost]或[HttpGet],那么提交方式要跟Action打的标签一致;

2.2 使用Microsoft AJAX方式

在ASP.Net MVC中除了可以使用JQuery AJAX外,Microsoft为我们提供了另一套实用且更简单的AJAX方案,我们姑且称其为:Microsoft AJAX方式。

(1)首先:

需要将微软提供的js脚本引入到页面中:其实就是jquery.unobtrusive-ajax.js

1
2
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>

确保在Web.config中启用了Unobtrusive JavaScript

1
<add key="UnobtrusiveJavaScriptEnabled" value="true" />

(2)其次,使用Ajax.BeginForm方法构造一个form表单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<h1>Microsoft Ajax方式</h1>
 @using (Ajax.BeginForm("GetServerDate", "Home", new AjaxOptions()
 {
     HttpMethod = "POST",
     Confirm = "您确定要提交?",
     InsertionMode = InsertionMode.Replace,
     UpdateTargetId = "spResult",
     OnSuccess = "afterSuccess",
     LoadingElementId="loading"
 }))
 {
     <table>
         <tr>
             <td>用户名:</td>
             <td>
                 <input id="txtUserName" name="UserName" /></td>
         </tr>
         <tr>
             <td>密   码:</td>
             <td>
                 <input id="txtPassword" name="Password" /></td>
         </tr>
         <tr>
             <td align="center" colspan="2">
                 <input id="btnAjax" type="submit" value="提 交" />
             </td>
         </tr>
         <tr>
             <td align="center" colspan="2">
                 <div id="loading" style="display:none">
                     <img style="vertical-align:middle" src="~/Content/ico_loading2.gif" />正在获取中,请稍候...
                 </div>
                 <span id="spResult"></span>
             </td>
         </tr>
     </table>
 }

这里需要注意的是:

①Ajax.BeginForm没有提供闭合的方法,需要使用Using配合关闭;

②AjaxOptions参数的设置:

HttpMethod代表此次AJAX请求到底是POST方式还是GET方式?这里是POST方式;

Confirm代表点击提交按钮后提出的确认对话框,并给出用户给定的提示语,这里是:您确定要提交?

InsertionMode代表请求获得后的数据是要替换还是追加,一般选择替换,即Replace;

UpdateTargetId代表需要替换的div标签的Id,这里是一个span标签,代表需要显示的信息都显示在这个span内;

OnSuccess代表请求成功后所需要执行的回调方法,是一个js方法,可以自定义,这里是一个function afterSuccess()的方法;

1
2
3
function afterSuccess(data) {
    //alert("您已成功获取数据:" + data);
}

LoadingElementId=”loading”是一个很有意思的属性,代表在ajax请求期间为了提供良好的用户体验,可以给出一个正在加 载中的提示,而这个LoadingElementId则代表一个提示的div区域的Id。这里主要是指id为loading的这个div,其中有一张 gif图片及一句话:正在获取中,请稍等…的提示。

1
2
3
<div id="loading" style="display:none">
      <img style="vertical-align:middle" src="~/Content/ico_loading2.gif" />正在获取中,请稍候...
</div>

为了显示加载提示的效果,我们人为地修改一下Action方法,使用Thread.Sleep(3000)来延迟一下请求返回时间

1
2
3
4
5
public ActionResult GetServerDate()
{
    System.Threading.Thread.Sleep(3000);
    return Content(DateTime.Now.ToString());
}

好了,现在我们可以看一下效果如何:

到此,我们的Microsoft AJAX就算完成了一个最简单的Demo了。那么,我们不禁想知道Microsoft AJAX是怎么做到的?跟校验一样,我们浏览一下生成的form表单就知道了:

原来我们在AjaxOptions中所设置的参数也被解析成了form的自定义属性,它们的对应关系如下:

三、为AOP而生 — ASP.Net MVC默认的过滤器

3.1 过滤器初步

  大一点的项目总会有相关的AOP面向切面的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中Action在执行前或者执行后我们想做一些特殊的操作(比如身份验证,日志,异常,行为截取等),而不想让MVC开发人员去关 心和写这部分重复的代码。那么,我们可以通过AOP截取实现,而在MVC项目中我们就可以直接使用它提供的Filter的特性帮我们解决,不用自己实现复 杂的AOP了。
AOP:Aspect Oriented Programming(AOP)是较为热门的一个话题。AOP,国内大致译作“面向切面编程”。针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
  利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。

3.2 微软提供的几种默认过滤器

微软默认为我们提供了四种类型的过滤器(Filter),如下图所示:

这里,我们主要来看看ActionFilter(Action过滤器)和ExceptionFilter(异常过滤器)的使用:

(1)Action Filter

ActionFilterAttribute默认实现了IActionFilter和IResultFilter。而 ActionFilterAttribute是一个Abstract的类型,所以不能直接使用,因为它不能实例化,所以我们想使用它必须继承一下它然后才 能使用。

①因此,我们首先在Models中新建一个类,取名为:MyActionFilterAttribute(以Attribute结尾比较符合编码规范),并使其继承自ActionFilterAttribute,然后重写基类所提供的虚方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class MyActionFilterAttribute : ActionFilterAttribute
{
    public string Name { get; set; }
 
    /// <summary>
    /// Action 执行之前先执行此方法
    /// </summary>
    /// <param name="filterContext">过滤器上下文</param>
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
        HttpContext.Current.Response.Write("OnActionExecuting :" + Name);
    }
 
    /// <summary>
    /// Action执行之后
    /// </summary>
    /// <param name="filterContext">过滤器上下文</param>
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        HttpContext.Current.Response.Write("OnActionExecuted :" + Name);
    }
 
    /// <summary>
    /// ActionResult执行之前先执行此方法
    /// </summary>
    /// <param name="filterContext">过滤器上下文</param>
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        base.OnResultExecuting(filterContext);
        HttpContext.Current.Response.Write("OnResultExecuting :" + Name);
 
    }
 
    /// <summary>
    /// ActionResult执行之后先执行此方法
    /// </summary>
    /// <param name="filterContext">过滤器上下文</param>
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        base.OnResultExecuted(filterContext);
        HttpContext.Current.Response.Write("OnResultExecuted :" + Name);
    }
}

这里我们重写了四个虚方法,他们各自代表了在Action执行之前和之后需要执行的业务逻辑,以及在Result执行之前和之后需要执行的业务逻 辑。这里的Result主要是指我们在Action中进行return 语句返回结果时(例如:return Content(“Hello Filter!”);),之前和之后要执行的逻辑处理。

比如:我们想要在每个Action执行之前进行用户是否登录的校验,可以在OnActionExecuting中判断用户Session是否存在,如果存在则继续执行Action的具体业务代码,如果不存在则重定向页面到登陆页,后边的Action业务代码不再执行。

②现在有了自定义的过滤器,我们怎么将其应用到Action中呢?这里有三种方式:

一是给某个控制器的某个Action指定此Filter:

1
2
3
4
5
6
[MyActionFilter(Name = "Filter Action")]
public ActionResult Filter()
{
    Response.Write("<p>Action正在努力执行中...</p>");
    return Content("<p>OK:视图成功被渲染</p>");
}

二是给某个控制器的所有Action指定此Filter:

1
2
3
4
[MyActionFilter(Name="Home Filter")]
public class HomeController : Controller
{
}

但是,要注意的是:如果既给Controller指定了Filter,又给该Controller中的某个Action指定了Filter,那么具体的这个Action以离其定义最近的Filter为准,也就是一个优先级的顺序问题:Action的Filter优先级高于Controller的Filter。

三是给此项目中的所有控制器即全局指定此Filter:在App_Start中更改FilterConfig类,此种方式优先级最低

1
2
3
4
5
6
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    // 注册自定义Action过滤器:优先级最低,但是可以作用到所有的控制器和Action
    filters.Add(new MyActionFilterAttribute() { Name = "Global Controller" });
}

③现在我们来看看具体的效果:

可以看到,我们的/Home/Filter这个Action中只有两句代码,一句Response.Write,另一句是return Content();在Response.Write之前执行了OnActionExecuting的过滤器方法,之后则执行了 OnActionExecuted的过滤器方法;我们刚刚说了,在Action中的return语句代表了Result,那么在Result之前执行了 OnResultExecuting过滤器方法,之后则执行了OnResultExecuted过滤器方法。这里仅仅是为了展示,在实际开发中是需要写一 些具体的业务逻辑处理的,例如:判断用户的登录状态,记录用户的操作日志等等。

(2)Exception Filter

①同样,在Models中新建一个类,取名为:MyExceptionFilterAttribute,并使其继承自HandleErrorAttribute。

1
2
3
4
5
6
7
8
9
public class MyExceptionFilterAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        base.OnException(filterContext);
 
        HttpContext.Current.Response.Redirect("/Home/Index");
    }
}

这里,重写基类的OnException方法,这里仅仅为了演示效果,没有对异常进行处理。在实际开发中,需要获取异常对象,并将其记录至日志中。例如,下面一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public override void OnException(ExceptionContext filterContext)
   {
       base.OnException(filterContext);
       //获取系统异常消息记录
       string strException = filterContext.Exception.Message;
       if (!string.IsNullOrEmpty(strException))
       {
           //使用Log4Net记录异常信息
           Exception exception = filterContext.Exception;
           if (exception != null)
           {
               LogHelper.WriteErrorLog(strException, exception);
           }
           else
           {
               LogHelper.WriteErrorLog(strException);
           }
       }
 
  filterContext.HttpContext.Response.Redirect("~/GlobalErrorPage.html");
   }

②有了异常过滤器,我们怎么来应用到项目中呢?答案也在App_Start中,还是在FilterConfig类中,新添一句代码进行注册:

1
2
3
4
5
6
7
8
9
10
11
public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        // 注册自定义Action过滤器:优先级最低,但是可以作用到所有的控制器和Action
        filters.Add(new MyActionFilterAttribute() { Name = "Global Controller" });
        // 注册自定义Exception过滤器
        filters.Add(new MyExceptionFilterAttribute());
    }
}

③为了测试,我们新增一个Action,使其能够出现一个异常:DividedByZero

1
2
3
4
5
6
7
public ActionResult Exception()
{
    int a = 10;
    int b = 0;
    int c = a / b;
    return Content("Exception is happened.");
}

④当我们测试这个Action时,会发现系统执行了自定义的异常过滤器,将我们的这个请求改为重定向到Index这个Action了。

参考资料

(1)蒋金楠,《ASP.NET MVC下的四种验证编程方式》,http://www.cnblogs.com/artech/p/asp-net-mvc-validation-programming.html

(2)蒋金楠,《ASP.NET MVC下的四种验证编程方式[续篇]》,http://www.cnblogs.com/artech/p/asp-net-mvc-4-validation.html

(3)马伦,《ASP.NET MVC 2014特供教程》,http://bbs.itcast.cn/thread-26722-1-1.html

(4)w809026418,《MVC中使用 DataAnnotations 进行模型验证》,http://www.cnblogs.com/haogj/archive/2011/11/16/2251920.html

(5)刘俊峰,《ASP.NET MVC中Unobtrusive Ajax的妙用》,http://www.cnblogs.com/rufi/archive/2012/03/31/unobtrusive-ajax.html

ASP.Net MVC开发基础学习笔记(4):校验、AJAX与过滤器的更多相关文章

  1. ASP.Net MVC开发基础学习笔记:一、走向MVC模式

    一.ASP.Net的两种开发模式 1.1 ASP.Net WebForm的开发模式 (1)处理流程 在传统的WebForm模式下,我们请求一个例如http://www.aspnetmvc.com/bl ...

  2. ASP.Net MVC开发基础学习笔记(1):走向MVC模式

    一.ASP.Net的两种开发模式 1.1 ASP.Net WebForm的开发模式 (1)处理流程 在传统的WebForm模式下,我们请求一个例如http://www.aspnetmvc.com/bl ...

  3. ASP.Net MVC开发基础学习笔记:二、HtmlHelper与扩展方法

    一.一个功能强大的页面开发辅助类—HtmlHelper初步了解 1.1 有失必有得 在ASP.Net MVC中微软并没有提供类似服务器端控件那种开发方式,毕竟微软的MVC就是传统的请求处理响应的回归. ...

  4. ASP.Net MVC开发基础学习笔记:三、Razor视图引擎、控制器与路由机制学习

    一.天降神器“剃须刀” — Razor视图引擎 1.1 千呼万唤始出来的MVC3.0 在MVC3.0版本的时候,微软终于引入了第二种模板引擎:Razor.在这之前,我们一直在使用WebForm时代沿留 ...

  5. ASP.Net MVC开发基础学习笔记:四、校验、AJAX与过滤器

    一.校验 — 表单不是你想提想提就能提 1.1 DataAnnotations(数据注解) 位于 System.ComponentModel.DataAnnotations 命名空间中的特性指定对数据 ...

  6. ASP.Net MVC开发基础学习笔记:五、区域、模板页与WebAPI初步

    一.区域—麻雀虽小,五脏俱全的迷你MVC项目 1.1 Area的兴起 为了方便大规模网站中的管理大量文件,在ASP.NET MVC 2.0版本中引入了一个新概念—区域(Area). 在项目上右击创建新 ...

  7. ASP.Net MVC开发基础学习笔记(5):区域、模板页与WebAPI初步

    一.区域—麻雀虽小,五脏俱全的迷你MVC项目 1.1 Area的兴起 为了方便大规模网站中的管理大量文件,在ASP.NET MVC 2.0版本中引入了一个新概念—区域(Area). 在项目上右击创建新 ...

  8. ASP.Net MVC开发基础学习笔记(3):Razor视图引擎、控制器与路由机制学习

    一.天降神器“剃须刀” — Razor视图引擎 1.1 千呼万唤始出来的MVC3.0 在MVC3.0版本的时候,微软终于引入了第二种模板引擎:Razor.在这之前,我们一直在使用WebForm时代沿留 ...

  9. ASP.Net MVC开发基础学习笔记(2):HtmlHelper与扩展方法

    一.一个功能强大的页面开发辅助类—HtmlHelper初步了解 1.1 有失必有得 在ASP.Net MVC中微软并没有提供类似服务器端控件那种开发方式,毕竟微软的MVC就是传统的请求处理响应的回归. ...

随机推荐

  1. 数据结构顺序表删除所有特定元素x

    顺序表类定义: template<class T> class SeqList : { public: SeqList(int mSize); ~SeqList() { delete[] ...

  2. MyBatis之多表关联查询

    1使用resultType.ResultMap处理返回结果 处理返回结果 resultType:指定返回值结果的完全限定名,处理多表查询的结果. 多表查询需要定义vo封装查询的结果. 需求:查询部门和 ...

  3. 让U盘永不中毒的解决办法

    一.背景: 在学校上课的时候,有个老师很潇洒的拿着一个U盘就来教室上课了.然后快上课的时候在电脑上准备播放课件.注意,这一瞬间其妙的事情发生了,课件因为他的U盘中病毒了,打不开了,老师当时笑了.后来又 ...

  4. August 10th, 2016, Week 33rd, Wednesday

    The degree of loving is measured by the degree of giving. 爱的深浅是用给与的多少来衡量的. Some say that if you love ...

  5. SQL Server之存储过程基础知

    什么是存储过程呢?存储过程就是作为可执行对象存放在数据库中的一个或多个SQL命令. 通俗来讲:存储过程其实就是能完成一定操作的一组SQL语句. 那为什么要用存储过程呢?1.存储过程只在创造时进行编译, ...

  6. 创建INnodb的compress表

    需要将innodb_file_per_table=1 ,innodb_file_format=Barracuda;; 如: Creating a Compressed Table in a Gener ...

  7. 6个原因说服你选择PostgreSQL9.6

    PostgreSQL9.6在前些日子发布了, 社区为该版本的重大更新付诸良多, 发布日志一如既往的长,我挑选了6个重要的更新, 这些或许能够帮助你更好的使用PostgreSQL. 并行: 并行应该是这 ...

  8. Hibernate中一对多和多对一关系

    1.单向多对一和双向多对一的区别? 只需要从一方获取另一方的数据时 就使用单向关联双方都需要获取对方数据时 就使用双向关系 部门--人员 使用人员时如果只需要获取对应部门信息(user.getdept ...

  9. ASP.NET Web Api 实现数据的分页(转载)

    转载地址:http://www.cnblogs.com/fzrain/p/3542608.html 前言 这篇文章我们将使用不同的方式实现手动分页(关于高端大气上档次的OData本文暂不涉及,但有可能 ...

  10. 在ubuntu上搭建开发环境8---Ubuntu搭建Android开发环境

    需要首先配置好JDK环境 参看:http://www.cnblogs.com/xumenger/p/4460055.html 安装Eclipse 在Android developer的官网上直接下载a ...