[.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(四)
一步步打造一个简单的 MVC 电商网站 - BooksStore(四)
本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore
《一步步打造一个简单的 MVC 电商网站 - BooksStore(一)》(发布时间:2017-03-30 )
《一步步打造一个简单的 MVC 电商网站 - BooksStore(二)》(发布时间:2017-03-31)
《一步步打造一个简单的 MVC 电商网站 - BooksStore(三)》(发布时间:2017-04-01)
《一步步打造一个简单的 MVC 电商网站 - BooksStore(四)》(发布时间:2017-04-05)
简介
上一节我们完成了两个主要功能:完成了整个购物车的流程,以及订单处理(发邮件进行通知),今天我们来学习一下最基本的增删改查,以及登录认证过滤器,加入防 CSRF 攻击,本系列已完结。
该系列主要功能与知识点如下:
分类、产品浏览、购物车、结算、CRUD(增删改查) 管理、发邮件、分页、模型绑定、认证过滤器和单元测试等。
【备注】项目使用 VS2015 + C#6 进行开发,有问题请发表在留言区哦,还有,页面长得比较丑,请见谅。
目录
- 基本的增删改查 CRUD
- 登录授权认证过滤
基本的增删改查 CRUD
我们创建一个新的控制器进行增删改查功能,AdminController,并添加一个显示所有数据的方法:

/// <summary>
/// 后台管理控制器
/// </summary>
public class AdminController : Controller
{
private readonly IBookRepository _bookRepository; public AdminController(IBookRepository bookRepository)
{
_bookRepository = bookRepository;
} /// <summary>
/// 首页
/// </summary>
/// <returns></returns>
public ActionResult Index()
{
return View(_bookRepository.Books);
}
}
不在沿用之前的布局页了,创建一个新的布局页 _AdmindLayout.cshtml:

<!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<link href="~/Contents/admin/Site.css" rel="stylesheet" />
</head>
<body>
<div>
@RenderBody()
</div>
</body>
</html>
.table {
width: 100%;
padding:;
margin:;
}
.table th {
font: bold 12px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
border-top: 1px solid #C1DAD7;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #CAE8EA no-repeat;
}
.table td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
font-size: 14px;
padding: 6px 6px 6px 12px;
color: #4f6b72;
}
.table td.alt {
background: #F5FAFA;
color: #797268;
}
.table th.spec, td.spec {
border-left: 1px solid #C1DAD7;
}
Site.css
对应的 Index.cshtml:
@model IEnumerable<Wen.BooksStore.Domain.Entities.Book>
@{
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<p>
@Html.ActionLink("新增", "Edit")
</p>
<table class="table">
<tr>
<th>
名称
</th>
<th>
描述
</th>
<th>
价格
</th>
<th>
分类
</th>
<th></th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Description)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.Category)
</td>
<td>
@Html.ActionLink("编辑", "Edit", new { id = item.Id })
@using (Html.BeginForm("Delete", "Admin", FormMethod.Post, new { style = "display:inline;" }))
{
@Html.Hidden("id", item.Id)
<input type="submit" value="删除" />
}
</td>
</tr>
}
</table>

编辑,我把新增和编辑的位置放在一块,使用 id 进行区分,如果 id = 0 就表示新增的信息。
在 AdminCtroller 中添加关于编辑的方法
/// <summary>
/// 编辑
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public ActionResult Edit(int id = )
{
if (id == )
{
return View(new Book());
} var model = _bookRepository.Books.FirstOrDefault(x => x.Id == id);
return View(model);
} /// <summary>
/// 编辑
/// </summary>
/// <param name="book"></param>
/// <returns></returns>
[HttpPost]
public ActionResult Edit(Book book)
{
if (!ModelState.IsValid)
{
return View(book);
} _bookRepository.SaveBook(book);
return RedirectToAction("Index");
}
更新存储库中的方法:

IBookRepository.cs
/// <summary>
/// 书存储库接口
/// </summary>
public interface IBookRepository
{
/// <summary>
/// 书模型集合
/// </summary>
IQueryable<Book> Books { get; } /// <summary>
/// 保存书
/// </summary>
/// <param name="book"></param>
/// <returns></returns>
int SaveBook(Book book); /// <summary>
/// 删除书
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Book DeleteBook(int id);
}
EfBookRepository.cs
/// <summary>
/// 书存储库
/// </summary>
public class EfBookRepository : IBookRepository
{
private readonly EfDbContext _context = new EfDbContext(); /// <summary>
/// 书模型集合
/// </summary>
public IQueryable<Book> Books => _context.Books; /// <summary>
/// 保存书
/// </summary>
/// <param name="book"></param>
/// <returns></returns>
public int SaveBook(Book book)
{
if (book.Id == )
{
_context.Books.Add(book);
}
else
{
var model = _context.Books.Find(book.Id); if (model==null)
{
return ;
} model.Category = book.Category;
model.Description = book.Description;
model.Name = book.Name;
model.Price = book.Price;
} return _context.SaveChanges();
} /// <summary>
/// 删除书
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Book DeleteBook(int id)
{
var model = _context.Books.Find(id); if (model == null)
{
return null;
} _context.Books.Remove(model);
_context.SaveChanges(); return model;
}
}
需要对 Book 模型加上验证用的特性:
[Table("Book")]
public class Book
{
/// <summary>
/// 标识
/// </summary>
public int Id { get; set; }
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "名称不能为空")]
public string Name { get; set; }
/// <summary>
/// 描述
/// </summary>
[Required(ErrorMessage = "描述不能为空")]
public string Description { get; set; }
/// <summary>
/// 价格
/// </summary>
[Required(ErrorMessage = "价格不能为空")]
[Range(0.01, double.MaxValue, ErrorMessage = "请填写合适的价格")]
public decimal Price { get; set; }
/// <summary>
/// 分类
/// </summary>
[Required(ErrorMessage = "分类不能为空")]
public string Category { get; set; }
}
_AdminLayout.cshtml 需要引入验证用的 js(客户端验证):
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
Edit.cshtml
@model Wen.BooksStore.Domain.Entities.Book
@{
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<h2>编辑</h2>
<div>
@Html.ValidationSummary()
<div>
@using (Html.BeginForm())
{
@Html.HiddenFor(x => x.Id)
<table>
<tr>
<td>名称</td>
<td>@Html.TextBoxFor(x => x.Name)</td>
</tr>
<tr>
<td>价格</td>
<td>@Html.TextBoxFor(x => x.Price)</td>
</tr>
<tr>
<td>分类</td>
<td>@Html.TextBoxFor(x => x.Category)</td>
</tr>
<tr>
<td>描述</td>
<td>@Html.TextAreaFor(x => x.Description)</td>
</tr>
</table>
<input type="submit" value="提交" />
}
</div>
</div>

图:错误提示

删除
/// <summary>
/// 删除
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpPost]
public ActionResult Delete(int id)
{
_bookRepository.DeleteBook(id);
return RedirectToAction("Index");
}
加入提示,我们在新增、编辑和删除时应该加入必要的提示信息,使用 TempData。

/Admin/Index.cshtml 下的也要添加:

执行效果:

【备注】TempData 临时数据保存了一条信息,是一个“键/值”字典,类似会话 Session 和 ViewBag,它和 Session 的差别是,在 HTTP 请求结束后会被删除。因为这里使用了 RedirectToAction ,一条重定向指令,会告诉浏览器重定向请求到一个新地址,这时就不能使用 ViewBag,ViewBag 用于在控制器与视图之间传递数据,但它保持数据的时间不能比当前的 HTTP 请求长,重定向意味着用户是跨请求的,ViewBag 不能用于跨请求时传递数据。
登录授权认证过滤
上面是一个 Admin 的后台管理操作,不是每一个用户都能够进入管理的,所以现在加入登录授权认证功能,只有成功后,才能进入管理界面。
先在配置文件 WebConfig.cs 中加入
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="">
<credentials passwordFormat="Clear">
<user name="admin" password=""/>
</credentials>
</forms>
</authentication>
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=301880
-->
<configuration>
<connectionStrings>
<add name="EfDbContext" connectionString="server=.;database=TestDb;uid=sa;pwd=123" providerName="System.Data.SqlClient"/>
</connectionStrings> <appSettings>
<add key="webpages:Version" value="3.0.0.0"/>
<add key="webpages:Enabled" value="false"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
<add key="SendEmailName" value="943239005@qq.com"/>
</appSettings>
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="">
<credentials passwordFormat="Clear">
<user name="admin" password=""/>
</credentials>
</forms>
</authentication>
<compilation debug="true" targetFramework="4.6.1"/>
<httpRuntime targetFramework="4.6.1"/>
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web"/>
</httpModules>
</system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="" compilerOptions="/langversion:6 /nowarn:1659;1699;1701"/>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"/>
</compilers>
</system.codedom>
<system.webServer> <validation validateIntegratedModeConfiguration="false"/>
<modules>
<remove name="ApplicationInsightsWebTracking"/>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web"
preCondition="managedHandler"/>
</modules>
</system.webServer>
</configuration>
WebConfig.cs
在这里使用的授权认证模式为表单认证,为了简化与数据库的交互操作,采取的是硬编码的形式。如果尚未得到认证,会跳转到 Account/Login 的地址让管理员先进行登录,timeout 表示登录(即认证)成功的保持时长为 2880 分钟(即 48 小时),而 name 表示的就是用户名, password 表示的就是登录密码。
这里采用的是授权认证过滤器,我们需要对要认证后才能进入的控制器添加一个特性[Authorize],即对 AdminController 添加该特性。

新建表单认证提供器,一个接口和一个实现:

IAuthProvider.cs:
public interface IAuthProvider
{
/// <summary>
/// 认证
/// </summary>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <returns></returns>
bool Auth(string userName, string password);
}
FormsAuthProvider.cs:
/// <summary>
/// 表单认证提供者
/// </summary>
public class FormsAuthProvider:IAuthProvider
{
/// <summary>
/// 认证
/// </summary>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <returns></returns>
public bool Auth(string userName, string password)
{
var result = FormsAuthentication.Authenticate(userName, password); if (result)
{
//设置认证 Cookie
FormsAuthentication.SetAuthCookie(userName, false);
} return result;
}
}
AddBindings() 方法中注册:
/// <summary>
/// 添加绑定
/// </summary>
private void AddBindings()
{
_kernel.Bind<IBookRepository>().To<EfBookRepository>();
_kernel.Bind<IOrderProcessor>().To<EmailOrderProcessor>();
_kernel.Bind<IAuthProvider>().To<FormsAuthProvider>();
}

/// <summary>
/// 登录视图模型
/// </summary>
public class LoginViewModel
{
[Required(ErrorMessage = "用户名不能为空")]
public string UserName { get; set; } [Required(ErrorMessage = "密码不能为空")]
[DataType(DataType.Password)]
public string Password { get; set; }
}
新建 AccountController

public class AccountController : Controller
{
private readonly IAuthProvider _authProvider; public AccountController(IAuthProvider authProvider)
{
_authProvider = authProvider;
} /// <summary>
/// 登录
/// </summary>
/// <returns></returns>
public ActionResult Login()
{
return View();
} /// <summary>
/// 登录
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
if (!ModelState.IsValid)
{
return View(new LoginViewModel());
} var result = _authProvider.Auth(model.UserName, model.Password);
if (result) return RedirectToAction("Index", "Admin"); ModelState.AddModelError("", "账号或用户名有误");
return View(new LoginViewModel());
}
}
Login.cshtml 登录页面:
@model Wen.BooksStore.WebUI.Models.LoginViewModel
@{
Layout = null;
} <!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
@*<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">*@
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
@*<link href="~/Contents/Login/css/htmleaf-demo.css" rel="stylesheet" />*@
<style type="text/css">
@@import url(https://fonts.googleapis.com/css?family=Roboto:300); .login-page {
margin: auto;
padding: % ;
width: 360px;
} .form {
background: #FFFFFF;
box-shadow: 20px rgba(, , , 0.2), 5px 5px rgba(, , , 0.24);
margin: auto 100px;
max-width: 360px;
padding: 45px;
position: relative;
text-align: center;
z-index: ;
} .form input {
background: #f2f2f2;
border: ;
box-sizing: border-box;
font-family: "Roboto", sans-serif;
font-size: 14px;
margin: 15px;
outline: ;
padding: 15px;
width: %;
} .form button {
-webkit-transition: all 0.3 ease;
background: #4CAF50;
border: ;
color: #FFFFFF;
cursor: pointer;
font-family: "Microsoft YaHei", "Roboto", sans-serif;
font-size: 14px;
outline: ;
padding: 15px;
text-transform: uppercase;
transition: all 0.3 ease;
width: %;
} .form button:hover, .form button:active, .form button:focus { background: #43A047; } .form .message {
color: #b3b3b3;
font-size: 12px;
margin: 15px ;
} .form .message a {
color: #4CAF50;
text-decoration: none;
} .form .register-form { display: none; } .container {
margin: auto;
max-width: 300px;
position: relative;
z-index: ;
} .container:before, .container:after {
clear: both;
content: "";
display: block;
} .container .info {
margin: 50px auto;
text-align: center;
} .container .info h1 {
color: #1a1a1a;
font-size: 36px;
font-weight: ;
margin: 15px;
padding: ;
} .container .info span {
color: #4d4d4d;
font-size: 12px;
} .container .info span a {
color: #;
text-decoration: none;
} .container .info span .fa { color: #EF3B3A; } body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
background: #76b852; /* fallback for old browsers */
background: -webkit-linear-gradient(right, #76b852, #8DC26F);
background: -moz-linear-gradient(right, #76b852, #8DC26F);
background: -o-linear-gradient(right, #76b852, #8DC26F);
background: linear-gradient(to left, #76b852, #8DC26F);
font-family: "Roboto", sans-serif;
}
</style>
<!--[if IE]>
<script src="http://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<![endif]-->
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
</head>
<body>
<div id="wrapper" class="login-page">
<div id="login_form" class="form">
@using (Html.BeginForm("Login", "Account", FormMethod.Post, new { @class = "login-form" }))
{
<span style="float: left; color: red;">@Html.ValidationSummary()</span>
@Html.AntiForgeryToken()
@Html.TextBoxFor(x => x.UserName, new { placeholder = "用户名" })
@Html.EditorFor(x => x.Password, new { placeholder = "密码", }) <input type="submit" value="登 录" />
} </div>
</div> </body>
</html>
Login.cshtml


【备注】ValidateAntiForgeryToken 特性用于防止跨站请求伪造(CSRF)攻击。
【博主】反骨仔
【原文】http://www.cnblogs.com/liqingwen/p/6658975.html
【改编与参考】《精通 ASP.NET MVC 5》
[.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(四)的更多相关文章
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(二)
一步步打造一个简单的 MVC 电商网站 - BooksStore(二) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 前: ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(三)
一步步打造一个简单的 MVC 电商网站 - BooksStore(三) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一)
一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一) (转)
http://www.cnblogs.com/liqingwen/p/6640861.html 一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:ht ...
- [.NET] 一步步打造一个简单的 MVC 网站 - BooksStore(一)
一步步打造一个简单的 MVC 网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 简介 主 ...
- 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户
阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...
- ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查
前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...
- 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑
阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...
- 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成
阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...
随机推荐
- [MongoDB] - Window7下安装及BSON基本介绍
MongoDB是一种NOSQL(Not Only SQL)类型的文档型数据库.NoSQL数据库与传统的关系型数据库相比,它具有操作简单.完全免费.源码公开.随时下载等特点,并可以用于各种商业目的.Mo ...
- TJOI2015 day1解题报告
博客园的编辑器真的是太蛋疼了= =,想用tex然后上jpg又贴不了链接,真的很纠结啊= = T1:[TJOI2015]线性代数 描述:戳上面吧= = 首先这道题我觉得是这套题最漂亮的一道题了(虽然说学 ...
- Python2.7学习笔记-定义函数、filter/map/reduce/lambda
我把写的代码直接贴在下面了,注释的不是很仔细,主要是为了自己复习时方便查找,并不适合没有接触过python的人看,其实我也是初学者. #定义函数 def my_abs(x): if x>=0: ...
- 【排序算法】快速插入排序算法 Java实现
基本思想 每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部插入完成. 设数组为a[0...n-1] 初始时,a[0]自成一个有序区,无序区为a[1...n-1] ...
- JavaScript两个变量交换值(不使用临时变量)
概要 本文主要描述,如何不使用中间值,将两个变量的值进行交换. 一.普通做法 var a = 1, b = 2, tmp; tmp = a; a = b; b = tmp; 普通的做法就是声明多一 ...
- List实现行转列的通用方案
最近在做报表统计方面的需求,涉及到行转列报表.根据以往经验使用SQL可以比较容易完成,这次决定挑战一下直接通过代码方式完成行转列.期间遇到几个问题和用到的新知识这里整理记录一下. 阅读目录 问题介绍 ...
- 使用SSM框架 搭建属于自己的APP二维码合成、解析、下载
最近公司的app上线了,在推广APP的时候出现了一个问题,因为Android和IOS的下载地址不一样,那么在推广的时候就要推广两个二维码,这样比较麻烦,如何简化我们的推广,让IOS用户扫描二维码的时候 ...
- 微信小程序怎样提高应用速度小技巧
作者:vicyao, 腾讯web前端开发 高级工程师商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. 原文链接:http://wetest.qq.com/lab/view/294.htm ...
- JS一周游~(基础、运算符、条件语句)
一.基础篇 JavaScript 基于浏览器(客户端).基于(面向)对象{没有继承}.事件驱动(要有对象).脚本语言(灵活多变) 1.作用 表单的验证,减轻服务端的压力 添加页面动画效果 动态更改页面 ...
- struts.xml语法
<!-- 1. struts.xml配置常量, 用来覆盖struts.properties中的默认常量配置 一般情况下, 这个配置放在struts.xml中, 不要放在各个模块的xml中. co ...