在asp.net mvc4中,微软带来一个新的东西Web Api,它只一种轻量化的REST风格服务,它比复杂的SOAP更加简洁,简单来说就是把事物分解成具体的资源,然后对资源进行增删改查,分别对应POST,DELETE,PUT,GET。当然我们也可以不依赖Web Api框架来实现自己的REST风格服务.
下面说说Web Api具体用法及一些Demo和遇到的问题,新建一个web api项目,来写一个Article服务.
public class ArticleController : ApiController { // GET api/ private IArticle articleDal = FYJ.Web.IocFactory.Instance; private IArticleView articleViewDal = FYJ.Web.IocFactory.Instance; public IComment CommentDal { get { return FYJ.Web.IocFactory.Instance; } } public IEnumerable Get() { GridPager pager = new GridPager(); IEnumerable list = articleViewDal.GetList(ref pager, null).Select(c => new blog_view_article { articleID = c.articleID, siteCategoryDisplay = c.siteCategoryDisplay, categoryDisplay = c.categoryDisplay, topicDisplay = c.topicDisplay, ADD_DATE = c.ADD_DATE, articleAddUserID = c.articleAddUserID, articleAttachmentLimit = c.articleAttachmentLimit, articleAuthor = c.articleAuthor, articleTitle = c.articleTitle, articleSubContentText = c.articleSubContentText }).ToList(); return list; } // GET api//5 public blog_view_article Get(int id) { blog_view_article m = articleViewDal.GetEntities().Where(c => c.articleID == id).FirstOrDefault(); return m; } //public HttpResponseMessage Get2(int id) //{ // blog_view_article m = articleViewDal.GetEntities().Where(c => c.articleID == id).FirstOrDefault(); // HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent("dddddd", Encoding.GetEncoding("UTF-8"), "application/json") }; // System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); // string json = serializer.Serialize(m); // return result; //} // POST api/ //public void Post([FromBody]string value) //{ //} public void Post([FromBody]blog_tb_article value) { } // PUT api//5 public void Put(int id, [FromBody]blog_tb_article value) { } // DELETE api//5 public void Delete(int id) { } }
这是修改后的ArticleController文件。 这里的Get、Put、Post、Delete方法不能重载,不然可能报错。
先说说Get方法,这里返回是一个IEnumerable对象,框架会将它序列化成json或者xml,当然你也可以定义自己的序列化。
默认情况下在ie中会输出json,在Chrome中可能会输出xml,这是因为请求的Accept不同造成的,我们让它全部返回json,只需要在Globl.asax.cs的 Application_Start方法中加入一句 GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
Get方法序列化有一个问题,如果是用的EF返回那么必须把EF代理关闭掉db.Configuration.ProxyCreationEnabled = false; 否则默认的序列化器无法序列化.
再说说自定义序列化器,定义一个类继承自MediaTypeFormatter 这个类,然后重写一些方法,具体就不再研究了
public class MyMediaTypeFormatter : MediaTypeFormatter { public override System.Threading.Tasks.Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext) { return base.WriteToStreamAsync(type, value, writeStream, content, transportContext); } public override System.Threading.Tasks.Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext, System.Threading.CancellationToken cancellationToken) { return base.WriteToStreamAsync(type, value, writeStream, content, transportContext, cancellationToken); } public override System.Threading.Tasks.Task ReadFromStreamAsync(Type type, System.IO.Stream readStream, System.Net.Http.HttpContent content, IFormatterLogger formatterLogger) { return base.ReadFromStreamAsync(type, readStream, content, formatterLogger); } public override System.Threading.Tasks.Task ReadFromStreamAsync(Type type, System.IO.Stream readStream, System.Net.Http.HttpContent content, IFormatterLogger formatterLogger, System.Threading.CancellationToken cancellationToken) { return base.ReadFromStreamAsync(type, readStream, content, formatterLogger, cancellationToken); } public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } }
然后再Globl.asax.cs的 Application_Start方法中加入
MyMediaTypeFormatter formatter = new MyMediaTypeFormatter();
GlobalConfiguration.Configuration.Formatters.Insert(0, formatter);
下面说说Post方法,这个纠结了很久,默认是
public void Post([FromBody]string value)
{
}
但是我在客户端Post的时候 这个value一直是null,将它参数改为对象
public void Post([FromBody]blog_tb_article value)
{
}
然后客户端的时候加上 req.ContentType = "application/json";这样的话对象能取到
Blogs.Entity.blog_tb_article model = new Blogs.Entity.blog_tb_article(); model.articleTitle = "测试标题"; System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); string json = serializer.Serialize(model); //System.Net.ServicePointManager.Expect100Continue = true; HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://service.ztku.com/api/Article/1102522"); req.KeepAlive = true; req.Method = "Put"; req.AllowAutoRedirect = true; req.ContentType = "application/json"; req.Headers.Add("key", "11234"); //req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; //req.Headers.Add("Accept-Encoding", "gzip,deflate"); //req.Connection = "keep-alive"; req.Timeout = 10000; byte[] postBytes = Encoding.UTF8.GetBytes(json); ; req.ContentLength = postBytes.Length; Stream st = req.GetRequestStream(); st.Write(postBytes, 0, postBytes.Length); st.Close(); HttpWebResponse res = (HttpWebResponse)req.GetResponse(); Stream receiveStream = res.GetResponseStream(); Encoding encode = Encoding.UTF8; if (res.CharacterSet != null && res.CharacterSet != "") { encode = Encoding.GetEncoding(res.CharacterSet); } StreamReader sr = new StreamReader(receiveStream, encode); string result = sr.ReadToEnd(); sr.Close();
Put方法也与Post方法类似.
在进行Put操作时,出现 远程服务器返回错误: (405) 不允许的方法,这个问题困扰了一上午,我的服务是挂在iis8.5上面,后来知道是因为装了一个WebDAV 的东西,在web.config配置中system.webServer 节点配置
runAllManagedModulesForAllRequests必须设为true
还有一个安全的问题需要考虑,服务端写一个类继承自DelegatingHandler 重写SendAsync方法
protected override System.Threading.Tasks.Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { int matchHeaderCount = request.Headers.Count((item) => { if ("key".Equals(item.Key)) { foreach (var str in item.Value) { if ("11234".Equals(str)) { return true; } } } return false; }); if (matchHeaderCount > 0) { return base.SendAsync(request, cancellationToken); } return Task.Factory.StartNew(() => { return new HttpResponseMessage(HttpStatusCode.Forbidden); }); }
这个是需要客户端的request中传入一个header
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://service.ztku.com/api/Article/1102522"); req.KeepAlive = true; req.Method = "Put"; req.AllowAutoRedirect = true; req.ContentType = "application/json"; req.Headers.Add("key", "11234");
这里的验证方式就可以有多种了,我们可以给客户端分配一个密钥,用这个密钥来对称加密一个每次都变的随机字符串和url 服务端再解密.
还有个问题 如果系统中还有mvc的话 Global可能需要注意一个注册先后问题
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
这里把api注册在前
当然webapi也可以脱离mvc使用的.