本篇体验在MVC4下,实现一个对Book信息的管理,包括增删查等,用到了EF Code First, 使用Unity进行依赖注入,前端使用Bootstrap美化。先上最终效果:
→创建一个MVC4项目,选择Web API模版。
→在Models文件夹创建一个Book.cs类。
namespace MyMvcAndWebApi.Models
{
public class Book
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
→在Models文件夹创建BookInitializer类,用来初始化数据库表数据。
using System.Data.Entity;
namespace MyMvcAndWebApi.Models
{
public class BookInitializer : DropCreateDatabaseIfModelChanges<BookStore>
{
protected override void Seed(BookStore context)
{
context.Books.Add(new Book() {Name = "我有一头小毛驴", Price = 200M});
context.Books.Add(new Book() { Name = "今天天气真好", Price = 300M });
context.Books.Add(new Book() { Name = "秋天是落叶的季节", Price = 500M });
}
}
}
→在Models文件夹中,创建BookStore类,派生于DbContext。
using System.Data.Entity;
namespace MyMvcAndWebApi.Models
{
public class BookStore : DbContext
{
public BookStore() : base("conn")
{
Database.SetInitializer(new BookInitializer());
}
public DbSet<Book> Books { get; set; }
}
}
→在Web.config中配置连接字符串。
<connectionStrings>
......
<add name="conn" connectionString="Data Source=.;User=yourusername;Password=yourpassword;Initial Catalog=BookStore;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
→Repository首先需要一个接口,在Models文件夹中创建IBookRepository接口。
using System.Collections.Generic;
namespace MyMvcAndWebApi.Models
{
public interface IBookRepository
{
IEnumerable<Book> GetAll();
Book Get(int id);
Book Add(Book book);
void Remove(int id);
bool Update(Book book);
}
}
→实现IBookRepository接口,用到BookStore这个上下文。
using System.Collections.Generic;
using System.Data;
namespace MyMvcAndWebApi.Models
{
public class BookRepository : IBookRepository
{
private BookStore db = new BookStore();
public BookRepository(){}
public IEnumerable<Book> GetAll()
{
return db.Books;
}
public Book Add(Book book)
{
db.Books.Add(book);
db.SaveChanges();
return book;
}
public void Remove(int id)
{
Book book = db.Books.Find(id);
db.Books.Remove(book);
db.SaveChanges();
}
public bool Update(Book book)
{
db.Entry(book).State = EntityState.Modified;
db.SaveChanges();
return true;
}
public Book Get(int id)
{
return db.Books.Find(id);
}
}
}
→有了接口和实现,接下来会用到依赖注入。选择使用Unity,在NuGet中下载。
→首先需要一个依赖注入容器。在项目下创建Helper文件夹,在其中创建IoCContainer类。
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;
using Microsoft.Practices.Unity;
namespace MyMvcAndWebApi.Helper
{
class ScopeContainer : IDependencyScope
{
protected IUnityContainer container;
public ScopeContainer(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
public object GetService(Type serviceType)
{
if (container.IsRegistered(serviceType))
{
return container.Resolve(serviceType);
}
else
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (container.IsRegistered(serviceType))
{
return container.ResolveAll(serviceType);
}
else
{
return new List<object>();
}
}
public void Dispose()
{
container.Dispose();
}
}
class IoCContainer : ScopeContainer, IDependencyResolver
{
public IoCContainer(IUnityContainer container)
: base(container)
{
}
public IDependencyScope BeginScope()
{
var child = container.CreateChildContainer();
return new ScopeContainer(child);
}
}
}
→在Global.asax中注册Unity。
sing System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using MyMvcAndWebApi.Controllers;
using MyMvcAndWebApi.Helper;
using MyMvcAndWebApi.Models;
namespace MyMvcAndWebApi
{
// 注意: 有关启用 IIS6 或 IIS7 经典模式的说明,
// 请访问 http://go.microsoft.com/?LinkId=9394801
public class WebApiApplication : System.Web.HttpApplication
{
//注册控制器,接口和实现
void ConfigureApi(HttpConfiguration config)
{
var unity = new UnityContainer();
unity.RegisterType<BooksController>();
unity.RegisterType<IBookRepository, BookRepository>(new HierarchicalLifetimeManager());
config.DependencyResolver = new IoCContainer(unity);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
//注册依赖注入
ConfigureApi(GlobalConfiguration.Configuration);
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
→创建一个空的Api控制器,编写如下:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using MyMvcAndWebApi.Models;
namespace MyMvcAndWebApi.Controllers
{
public class BooksController : ApiController
{
//_repository运行时变量,在首次引用IBookRepository方法时动态分配内存
private static IBookRepository _repository;
public BooksController(IBookRepository repository)
{
if (repository == null)
{
throw new ArgumentNullException("repository");
}
_repository = repository;
}
//根据惯例,如果action名称以Get开头,那就接收Get请求
public IEnumerable<Book> GetAllBooks()
{
return _repository.GetAll();
}
//ASP.NET Web API会自动帮我们把URL中的字符串id转换成参数类型int
public Book GetBook(int id)
{
Book book = _repository.Get(id);
if (book == null)
{
//HttpResponseException封装返回的异常
//HttpResponseMessage封装返回的信息
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
return book;
}
//添加
//action名以post开头,按照惯例,接收post请求
//客户端发送来序列化的Book对象,在服务端对Book对象反序列化
public HttpResponseMessage PostBook(Book book)
{
book = _repository.Add(book);
// Web API默认返回的状态码为200,可是,根据HTTP/1.1协议,在添加完,我们希望返回201状态码
var response = Request.CreateResponse(HttpStatusCode.Created, book);
//返回新创建资源的url
string uri = Url.Route(null, new {id = book.Id});
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
}
//修改
//参数id从url中获取,book从request中反序列化
//根据惯例,Put开头的action,接收put请求
public void PutBook(int id, Book book)
{
book.Id = id;
if (!_repository.Update(book))
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
//删除
//根据管理,Delete开头接收delete请求
public HttpResponseMessage DeleteBook(int id)
{
_repository.Remove(id);
//返回200状态码表示删除成功
//返回202状态码表示正在删除
//返回204状态码表示没有内容
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
}
}
以上,所有action方法名称都符合了惯例。
→修改Home/Index.cshtml,我们在此使用jquery与服务端api控制器进行交互。
@section scripts {
<script src="@Url.Content("~/Scripts/jquery-1.6.2.js")" type="text/javascript"> </script>
<script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"> </script>
<script src="@Url.Content("~/Scripts/jQuery.tmpl.js")" type="text/javascript"> </script>
<script type="text/javascript">
$(function () {
//Get请求
$.getJSON(
"api/books",
function (data) {
$.each(data,
function (index, value) {
$("#bookTemplate").tmpl(value).appendTo("#books");
}
);
$("#loader").hide("slow");
$("#addBook").show("slow");
}
);
//添加
$("#addBook").submit(function () {
$.post(
"api/books",
$("#addBook").serialize(), //序列化Book对象
function (value) {
$("#bookTemplate").tmpl(value).appendTo("#books");
$("#name").val("");
$("#price").val("");
},
"json"
);
return false;
});
//删除
$(".removeBook").live("click", function () {
$.ajax({
type: "DELETE",
url: $(this).attr("href"),
context: this,
success: function () {
$(this).closest("li").remove();
}
});
return false;
});
$("input[type=\"submit\"], .removeBook, .viewImage").button();
});
//根据id搜索
function find() {
var id = $('#bookId').val();
$.getJSON("api/books/" + id,
function (data) {
var str = data.Name + ': $' + data.Price;
$('#book').html(str);
})
.fail(
function (jqXHR, textStatus, err) {
$('#book').html('Error: ' + err);
});
}
</script>
<script id="bookTemplate" type="text/html">
<li>
<p>
<strong> Book ID:</strong> ${ Id}
<br />
<strong> Book Name:</strong> ${ Name }
<br />
<strong> Price: $</strong> ${ Price }
</p>
<p>
<a href="${ Self }" class="button small red removeBook">移除</a>
</p>
</li>
</script>
}
<body>
<form method="post" id="addBook">
<div class="container_16">
<h1 class="title-01">Book信息</h1>
</div>
<div class="container_16">
<div class="grid_16 body-container">
<div class="margin grid_6 alpha">
<label for="Name">
Name</label><br />
<input type="text" id="name" name="Name" class="text grid_4" />
<br class="clear" />
<label for="Price">
Price</label><br />
<input type="text" id="price" name="Price" class="text grid_4" />
<br class="clear" />
<input type="submit" value="添加" class="button small green" />
<br />
<br />
<br class="clear" />
<strong id="book">@* <label id="book">
</label>*@ </strong>
<br />
<br class="clear" />
<br />
<label for="bookId">
根据ID搜索
</label>
<br />
<input type="text" id="bookId" size="20" class="text grid_4" /><br class="clear" />
<input type="button" value="搜索" onclick="find();" class="button small gray" />
</div>
<div class="grid_8 omega">
<img id="loader" src="images/ajax-loader.gif" />
<ul id="books" class="books">
</ul>
</div>
</div>
</div>
<br class="clear" />
<div class="footer clearfix">
</div>
</form>
</body>
另外,有关Bootsrap的样式在BundleConfig类中定义。
参考资料:http://www.codeproject.com/Articles/344078/ASP-NET-WebAPI-Getting-Started-with-MVC-and-WebAP