在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使用的.
珂珂的个人博客 - 一个程序猿的个人网站