作为没有花很多时间转java,把java当C#用的我,在做服务器端程序的时候,自然不想考虑java web,java需要学的框架太多了,看了一下Java Servlet,始终没有编码的冲动。经过几天的试验还是决定使用.net做服务器端。webservic很简单也经常用,WCF也用过,感觉还是没有web api来得轻量级,而且根据经验一般商用传统软件会有一大群小白用户和计算机能力不强的代理商,他们都要求软件要部署安装调试简便,什么装个新的Windows Server,新的MSSQL,设置IIS...最好是下一步下一步搞定这种,这就要求服务器端一般吧依赖IIS,这个要求也不过份。那么调研下来只有mvc webapi 2+.net 4.5(OWin.Net)可以自宿主host web服务。
一路趟坑下来,总算成功了。记录下来给朋友们参考一下。
1、win7 64位系统装不上vs2013,.net 4.5的限制
2、OWIN.NET + .net 4尝试
3、web api 的路由把我坑惨了,路由机制跟mvc不一样,在写api的时候传参遇到的问题
1、win7 64位系统装不上vs2013,.net 4.5的限制
安装vs2013需要IE10以上还需要安装SP1,尝试几次安装SP1都是在最后关头失败了,至今没有解决,结果在公司的win2008上安装vs2013成功。由于现在还是使用vs2010比较多,所以家里的电脑就不折腾了。还了解到.net 4.5支持的操作系统有: WIN7 SP1以上,WIN2008 SP1以上,WIN2012,Win8;后两个系统都没用过,貌似这个方案也不是很靠谱啊,客户一般XP和WIN7,服务器WIN2003或者WIN2008比较多。勉强可以吧,服务器端程序要求Win7以上供应商应该能够接受。
2、OWIN.NET + .net 4尝试
这个尝试是把.net 4.5和owin.net需要的全部dll添加到.net 4的解决方案里面,经过N次排错之后编译通过。这个比较奇怪在家里的电脑上(没有安装.net4.5)编译通过,在公司的电脑上(vs2010和vs2013共存)编译失败,多出来N多错误,排除不了。这不是天方夜谭,也不是天马行空的想法,因为之前有.net 2使用linq的经验,所以想尝试一下,结果死在了沙滩上。
owin.net依赖.net 4.5才有的一个方法,现在那个解决方案的源码找不到了,忘记什么异常了。总之一时解决不了.net 4.5的依赖,失败了。
3、web api 的路由把我坑惨了,路由机制跟mvc不一样,在写api的时候传参遇到的问题
1)web api不是我想的那样: web api就是没有view的mvc!!!
之前学过点mvc用mvc 2做过项目,于是稍微查下资料就给web api下了个结论:"web api就是没有view的mvc",就是这个结论让我趟了许多坑,在写api的时候调试遇到各种问题,比如多个参数,多种参数类型api返回json等问题,以前用mvc的JsonResult很顺手,结果非常不习惯web api返回json(这是不了解web api机制导致的),原来web api支持xml和json两种格式的返回,一番恶补之后,终于走上正途了。哎,基础啊基础,自作孽不可活啊,不要以为跟着一些简单的例子操作一遍就掌握了,没基础没有知其所以然就是不行啊。
2)web api的默认路由规则是省略了action的名称,使用默认的路由“api/{controller}/{id}"我不明觉厉,发现api传一个参数的时候可以接收到,但是多个参数不行,自定义路由参数方式好像可以,但是参数名和个数变化大,发现是没有搞明白和mvc的路由之间的差异造成的。
api/{controller}/{id}这个看了N次解释后才了解清楚 {controller}代表控制器的名称,然后自己就传参{id}了,{action}哪儿去了?读书看帖不求甚解啊,罪过,这就是自以为是的苦果,一直都是先入为主,以为是那样的,结果最基础的特性都没有了解清楚。总之将{action}加入变成“api/{controller}/{action}/{id}"之后一切都顺利了。
最终成果及关键代码分享
1)先截几个图吧
服务器端:
客户端:
2)关键代码,Win Service方式Host web api。
定制路由规则和json方式默认返回。
namespace SaunaWebApiHostService { public class RegisterRoutesStartup { public void Configuration(IAppBuilder appBuilder) { // Configure Web API for self-host. 默认路由 HttpConfiguration config = new HttpConfiguration(); //config.Routes.MapHttpRoute( // name: "DefaultApi", // routeTemplate: "api/{controller}/{id}", // defaults: new { id = RouteParameter.Optional } //); //自定义路由 config.Routes.MapHttpRoute( name: "CustomApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); //只响应Json请求 var jsonFormatter = new JsonMediaTypeFormatter(); config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter)); appBuilder.UseWebApi(config); } } }
Windows Service
/// <summary> /// 启动服务 /// </summary> /// <param name="args"></param> public new void OnStart(string[] args) { hostObject = WebApp.Start<RegisterRoutesStartup>(this.GetBaseAddress()); EventLog.WriteEntry("Web Api Host Success,请求基地址:" + this.GetBaseAddress() + "。", EventLogEntryType.Information); //开始主线程 mainTaskThread = new Thread(SearchDataChangeEvent); mainTaskThread.Start(); }
如果服务没有其他事做但是host webapp,好像要被系统认为服务没有任何事情可做会自动结束。所以开了个线程,轮询管理系统的消费清单,如果发生变化udp发送udp广播给终端和各消费点方便刷新显示。
安卓客户端:
@Override protected Boolean doInBackground(String... params) { List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(); nameValuePairs.add(new BasicNameValuePair("timestamp", String .valueOf(System.currentTimeMillis()))); JSONHttpClient jsonHttpClient = new JSONHttpClient(); String strResult = jsonHttpClient.Get(ServiceUrl.getInstance("") .get_Categary_URL(), nameValuePairs); resultList = new GsonBuilder() .enableComplexMapKeySerialization() .create() .fromJson(strResult, new TypeToken<List<SaunaItemCategory>>() { }.getType()); return resultList.size() > 0 ? true : false; }
package com.aidpoint.sauna.service; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.List; import java.util.zip.GZIPInputStream; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import android.util.Log; import com.aidpoint.sauna.util.ServiceUrl; import com.google.gson.GsonBuilder; public class JSONHttpClient { public <T> T PostObject(final String url, final T object, final Class<T> objectClass) throws Exception { DefaultHttpClient defaultHttpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost(url); try { String data = new GsonBuilder().create().toJson(object); StringEntity stringEntity = new StringEntity(data); Log.i(ServiceUrl.G_Log_Flag, "Post Request:" + data); httpPost.setEntity(stringEntity); httpPost.setHeader("Accept", "application/json"); httpPost.setHeader("Content-type", "application/json"); httpPost.setHeader("Accept-Encoding", "gzip"); HttpResponse httpResponse = defaultHttpClient.execute(httpPost); Log.i(ServiceUrl.G_Log_Flag, "Response Result Status:" + httpResponse.getStatusLine()); if (httpResponse.getStatusLine().getStatusCode() == 200) { HttpEntity httpEntity = httpResponse.getEntity(); if (httpEntity != null) { InputStream inputStream = httpEntity.getContent(); org.apache.http.Header contentEncoding = httpResponse .getFirstHeader("Content-Encoding"); if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase( "gzip")) { inputStream = new GZIPInputStream(inputStream); } String resultString = convertStreamToString(inputStream); inputStream.close(); return new GsonBuilder().create().fromJson(resultString, objectClass); } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); throw e; } catch (ClientProtocolException e) { e.printStackTrace(); throw e; } catch (IOException e) { e.printStackTrace(); throw e; } return null; } public <T> T PostParams(String url, final List<NameValuePair> params, final Class<T> objectClass) throws Exception { String paramString = URLEncodedUtils.format(params, "utf-8"); url += "?" + paramString; return PostObject(url, null, objectClass); } private String convertStreamToString(InputStream inputStream) { BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(inputStream)); StringBuilder stringBuilder = new StringBuilder(); String line = null; try { while ((line = bufferedReader.readLine()) != null) { stringBuilder.append(line + " "); } } catch (IOException e) { e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } return stringBuilder.toString(); } public <T> T Get(String url, List<NameValuePair> params, final Class<T> objectClass) { DefaultHttpClient defaultHttpClient = new DefaultHttpClient(); String paramString = URLEncodedUtils.format(params, "utf-8"); url += "?" + paramString; Log.i(ServiceUrl.G_Log_Flag, "Send Request:" + url); HttpGet httpGet = new HttpGet(url); try { httpGet.setHeader("Accept", "application/json"); HttpResponse httpResponse = defaultHttpClient.execute(httpGet); Log.i(ServiceUrl.G_Log_Flag, "Response Result Status:" + httpResponse.getStatusLine()); if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == 200) { HttpEntity httpEntity = httpResponse.getEntity(); if (httpEntity != null) { InputStream inputStream = httpEntity.getContent(); org.apache.http.Header contentEncoding = httpResponse .getFirstHeader("Content-Encoding"); String resultString = convertStreamToString(inputStream); inputStream.close(); return new GsonBuilder().enableComplexMapKeySerialization() .create().fromJson(resultString, objectClass); } else return null; } else return null; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } catch (ClientProtocolException e) { e.printStackTrace(); return null; } catch (IOException e) { e.printStackTrace(); return null; } } public String Get(String url, List<NameValuePair> params) { DefaultHttpClient defaultHttpClient = new DefaultHttpClient(); String paramString = URLEncodedUtils.format(params, "utf-8"); url += "?" + paramString; Log.i(ServiceUrl.G_Log_Flag, "Send Request:" + url); HttpGet httpGet = new HttpGet(url); try { httpGet.setHeader("Accept", "application/json"); HttpResponse httpResponse = defaultHttpClient.execute(httpGet); Log.i(ServiceUrl.G_Log_Flag, "Response Result Status:" + httpResponse.getStatusLine()); if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == 200) { HttpEntity httpEntity = httpResponse.getEntity(); if (httpEntity != null) { InputStream inputStream = httpEntity.getContent(); org.apache.http.Header contentEncoding = httpResponse .getFirstHeader("Content-Encoding"); String resultString = convertStreamToString(inputStream); inputStream.close(); return resultString; } else return null; } else return null; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } catch (ClientProtocolException e) { e.printStackTrace(); return null; } catch (IOException e) { e.printStackTrace(); return null; } } public boolean Delete(String url, final List<NameValuePair> params) { DefaultHttpClient defaultHttpClient = new DefaultHttpClient(); String paramString = URLEncodedUtils.format(params, "utf-8"); url += "?" + paramString; Log.i(ServiceUrl.G_Log_Flag, url); HttpDelete httpDelete = new HttpDelete(url); HttpResponse httpResponse = null; try { httpResponse = defaultHttpClient.execute(httpDelete); return httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT; } catch (IOException e) { e.printStackTrace(); } return false; } }