C# 服务端篇之实现RestFul Service开发(简单实用)
一、RestFul简介
REST(Representational State Transfer 通常被翻译为“表述性状态传输”或者“表述性状态转移”)是RoyFielding提出的一个描述互联系统架构风格的名词。为什么称为REST?Web本质上由各种各样的资源组成,资源由URI 唯一标识。浏览器(或者任何其它类似于浏览器的应用程序)将展示出该资源的一种表现方式,或者一种表现状态。如果用户在该页面中定向到指向其它资源的链接,则将访问该资源,并表现出它的状态。这意味着客户端应用程序随着每个资源表现状态的不同而发生状态转移,也即所谓REST。
简单地来说REST它是一种使用URL来定位资源,使用HTTP请求描述操作的Web服务规范。REST主要包括以下几方面:
(1) REST是一组架构约束条件和原则,而满足这些约束条件和原则的应用程序就是RESTful。
(2)REST的目标是构建可扩展的Web Service,它是一种更简单的SOAP(Simple Object Access Protocol)协议以及以WSDL为基础的WebService的替代。
(3)REST采用的是HTTP协议并通过HTTP中的GET、POST、PUT、DELETE等动词收发数据。
(4) REST希望通过HTTP来完成对数据的元操作,即传统的CRUD(Create、Read、Update、Delete)分别对应GET、POST、PUT、DELETE,这样就统一了数据操作的接口,实现在不同平台上提供一套相同的服务。
(5) REST是一种面向服务的、分布式的API设计风格。
RESTful API的开发和使用,无非是客户端向服务器发请求(request),以及服务器对客户端请求的响应(response)。所以RESTful架构风格具有统一接口的特点,即:使用不同的http方法表达不同的行为:
- GET(SELECT):从服务器取出资源(一项或多项)
- POST(CREATE):在服务器新建一个资源
- PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)
- PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)
- DELETE(DELETE):从服务器删除资源
二、REST的约束条件和原则
REST本质上是Web服务的一种规范,一种思想。它主要包括以下特性:
1、资源(Resources)
在REST中资源是整个架构或者说整个网络处理的核心,那么什么又是资源呢?在我们传统的观念中,资源是指服务器上的一个文件,而在REST里资源则是指一个URL。URL即统一资源定位,而我们都知道通过URL可以访问互联网上的资源,所以在REST里这种对资源的指向性更加强烈,并且在这里资源的范畴会被无限放大而并非局限在文件本身,例如以下实例:
http://api.cnblogs.com/info/source 表示获取某人的成绩
http://api.cnblogs.com/info/friends 表示获取某人的好友列表
http://api.cnblogs.com/info/profile 表示获取某人的详细信息
由此我们注意到REST在形式上更加趋向API设计,而我们获取的资源则通过一定的形式进行统一而规范化的表达,因此REST实现了让不同的平台共享一套API这样的愿望,这是一件非常美好的事情,这个世界上的技术阵营举不胜数,而它们为了各自的利益建立一套封闭、臃肿的体系框架,很多时候当我们不需要这样的“全家桶”并且希望“跨平台”的时候,REST将会是一个不错的选择。
2、表现形式(Representational)
在REST中表现形式作为我们对资源请求的一个结果的呈现,通过对HTTP协议的学习我们已经知道,服务器会给客户端返回什么形式的信息,这一点取决于服务器响应报文中相关头部字段,而对REST来讲,它通常会采用XML或者JSON来告诉请求者请求的结果,因为JSON相比XML所含的冗余信息较少,所以目前更加倾向于或者说流行使用JSON作为请求结果的表现形式。
3、状态变化(State Transfer)
虽然我们一再强调HTTP协议是无状态,这主要体现在HTTP请求与请求、HTTP响应与响应的上下文无关性上。在REST中,我们所说状态变化更多是指HTTP中的GET、POST、PUT、DELETE等动词实现。具体来讲,看下面的简单示例:
GET http://url/info 表示获取全部的info
POST http://url/info 表示创建一个新的info
GET http://url/info/{id} 表示获取一个指定id的info
PUT http://url/info/{id} 表示更新一个指定id的info
DELETE http://url/info/{id} 表示删除一个指定id的info
除此之外,我们注意到REST基于HTTP协议,所以HTTP协议中的状态码对它来讲同样适用,例如最常用的200表示成功、500表示服务器内部错误、404表示无法找到请求资源、400表示请求错误等等。
三、如何构建RestFul风格的API
如何构建REST风格的API?我们可以通过以下实例说明
- URLRoot采用下面这样的结构:
http://example.com/api/v1/
http://api.example.com/v1/
- API版本可以放在URL或者HTTP的Header里
- URL使用名词而非动词:
http://example.com/api/v1/getProducts 这是一个糟糕的设计
GET http://example.com/api/v1/products 这是一个优雅的设计
- 保证方法时安全的不会对资源状态有所改变。例如:
GET http://example.com/api/v1/deleteProduct?id=1 这是一个危险的信号
- 资源的地址推荐使用嵌套结构
GET http://example.com/api/v1/friends/123456789/profile
- 使用正确的HTTP状态码表示访问状态
- 返回含义明确的结果(一般推荐JSON)
四、Restful 服务端
下面主要讲解如何用C#实现一个Rest 风格的web服务供外部调用,主要包括以下4点:
- 定义service的契约
- 定义URL Routing
- 实现service
- 为服务编写宿主程序
1、定义service的契约和URL Routing
先定义服务契约,这里我介绍最常见的两种方式,分别采用GET和POST方式访问,使用VS2015创建一个新的控制台工程,命名为RestFulService。如图所示:
并为该工程添加引用System.ServiceModel 和System.ServiceModel.Web
创建一个接口类文件,命名为IPersonInfoQuery。为了让.Net FrameWork识别这是一个service接口,我们需要给接口添加上ServiceContract特性,并且给接口定义的方法添加OperationContract特性。这里我介绍最常见的两种方式,分别采用GET和POST方式访问。我们可以看到,与普通WCF服务契约不同的是,需要额外用WebGet或者WebInvoke指定REST访问的方式。另外还要指定消息包装样式和消息格式,默认的消息请求和响应格式为XML,若选择JSON需要显式声明。 UriTemplate用来将方法映射到具体的Uri上,但如果不指定映射,将映射到默认的Uri。比如采用Get访问的GetUser方法,默认映射是:/GetSource?Name={Name}
我们定义两种方法,1、GetScore方法:通过GET请求传入name,返回对应的成绩;2、GetInfo方法:通过POST请求,传入Info对象,查找对应的User并返回给客户端,代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Threading.Tasks; namespace RestFulService
{
/// <summary>
/// 简单定义两种方法,1、GetScore方法:通过GET请求传入name,返回对应的成绩;2、GetInfo方法:通过POST请求,传入Info对象,查找对应的User并返回给客户端
/// </summary>
[ServiceContract(Name = "PersonInfoQueryServices")]
public interface IPersonInfoQuery
{
/// <summary>
/// 说明:GET请求
/// WebGet默认请求是GET方式
/// UriTemplate(URL Routing)的参数名name必须要方法的参数名必须一致(不区分大小写)
/// RequestFormat规定客户端必须是什么数据格式请求的(JSon或者XML),不设置默认为XML
/// ResponseFormat规定服务端返回给客户端是以是什么数据格返回的(JSon或者XML)
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[OperationContract]
[WebGet(UriTemplate = "PersonInfoQuery/{name}", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
User GetScore(string name); /// <summary>
/// 说明:POS请求
/// WebInvoke请求方式有POST、PUT、DELETE等,所以需要明确指定Method是哪种请求的,这里我们设置POST请求。
/// 注意:POST情况下,UriTemplate(URL Routing)一般是没有参数(和上面GET的UriTemplate不一样,因为POST参数都通过消息体传送)
/// RequestFormat规定客户端必须是什么数据格式请求的(JSon或者XML),不设置默认为XML
/// ResponseFormat规定服务端返回给客户端是以是什么数据格返回的(JSon或者XML)
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "PersonInfoQuery/Info", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
User GetInfo(Info info);
}
}
定义对象User
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization; namespace RestFulService
{
[DataContract]
public class User
{
[DataMember]
public int ID { get; set; } [DataMember]
public string Name { get; set; } [DataMember]
public int Age { get; set; } [DataMember]
public int Score { get; set; }
}
}
定义对象Info
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks; namespace RestFulService
{
[DataContract]
public class Info
{
[DataMember]
public int ID { get; set; } [DataMember]
public string Name { get; set; }
}
}
说明:
UriTemplate就是我们之前提到的URL Routing(可以单独创建一个Routing进行管理)
WebGet默认请求是GET方式。
WebInvoke请求方式有POST、PUT、DELETE等,所以需要明确指定Method是哪种请求的。
UriTemplate(URL Routing)的参数名name必须要方法的参数名必须一致(不区分大小写)
POST情况下,UriTemplate(URL Routing)一般是没有参数(和GET的UriTemplate不一样,因为POST参数都通过消息体传送)
RequestFormat规定客户端必须是什么数据格式请求的(JSon或者XML),不设置默认为XML
ResponseFormat规定服务端返回给客户端是以是什么数据格返回的(JSon或者XML)
2、实现service
上面我们定义了接口,那么我们需要创建一个服务去实现这个接口的方法,我们创建一个类名为PersonInfoQueryServices,我们需要设置一些ServiceBehavior特征属性。代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Text;
using System.Threading.Tasks; namespace RestFulService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single, IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class PersonInfoQueryServices : IPersonInfoQuery
{
private List<User> UserList = new List<User>();
/// <summary>
/// 生成一些测试数据
/// </summary>
public PersonInfoQueryServices()
{
UserList.Add(new User() { ID = , Name = "张三", Age = , Score = });
UserList.Add(new User() { ID = , Name = "李四", Age = , Score = });
UserList.Add(new User() { ID = , Name = "王二麻子", Age = , Score = });
}
/// <summary>
/// 实现GetScore方法,返回某人的成绩
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public User GetScore(string name)
{
return UserList.FirstOrDefault(n => n.Name == name);
}
/// <summary>
/// 实现GetInfo方法,返回某人的User信息
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public User GetInfo(Info info)
{
return UserList.FirstOrDefault(n => n.ID == info.ID && n.Name == info.Name);
} }
}
3、服务编写宿主程序
上面我们定义了Service接口,并实现了Service方法,现在我们需要将编写宿主程序,以便能够使客户端通过GET或者POST方法进行请求。代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.Text;
using System.Threading.Tasks; namespace RestFulService
{
class Program
{
static void Main(string[] args)
{
try
{
PersonInfoQueryServices service = new PersonInfoQueryServices();
Uri baseAddress = new Uri("http://127.0.0.1:7788/");
using (ServiceHost _serviceHost = new ServiceHost(service, baseAddress))//或者:WebServiceHost _serviceHost = new WebServiceHost(typeof(PersonInfoQueryServices), baseAddress);
{
//如果不设置MaxBufferSize,当传输的数据特别大的时候,很容易出现“提示:413 Request Entity Too Large”错误信息,最大设置为20M
WebHttpBinding binding = new WebHttpBinding
{
TransferMode = TransferMode.Buffered,
MaxBufferSize = ,
MaxReceivedMessageSize = ,
MaxBufferPoolSize = ,
ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max,
Security = { Mode = WebHttpSecurityMode.None }
};
_serviceHost.AddServiceEndpoint(typeof(IPersonInfoQuery), binding, baseAddress);
_serviceHost.Opened += delegate
{
Console.WriteLine("Web服务已开启...");
};
_serviceHost.Open();
Console.WriteLine("输入任意键关闭程序!");
Console.ReadKey();
_serviceHost.Close();
}
}
catch (Exception)
{
Console.WriteLine("Web服务开启失败:{0}\r\n{1}", ex.Message, ex.StackTrace);
}
}
}
如果上面的ServiceHost服务启动了,但是提示服务却找不到相对应的接口,可以使用下面服务方式(简单明了~)
//使用新的方式
WebServiceHost _serviceHost = new WebServiceHost(service, baseAddress);
_serviceHost.Open();
Console.WriteLine("Web服务已开启...");
Console.WriteLine("输入任意键关闭程序!");
Console.ReadKey();
_serviceHost.Close();
开启效果如下:
最后,我们通过浏览器简单的测试一下GET请求的效果是怎样的,如图所示:
PS:更多Restful的C#规范,可以参考微软的文档--->WebGet和WebInvoke传送门 API传送门
五、Restful 客户端
上面我们已经简单的成功实现了Restful Service,下面我们简单的讲解一下,如何实现Restful 客户端来校验上面的Restful 服务器的正确性。PS:如何定义高效便捷的Restful Client帮助类,我们将在下篇文章进行讲解,本文就先介绍一种简单有效的Restful Client做一个Demo测试。
我们定义一个Restful 客户端的帮助类RestClient,用于和Restful 服务端交互,如图所示:
、
帮助类代码如下:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks; namespace RestFulClient
{
/// <summary>
/// 请求类型
/// </summary>
public enum EnumHttpVerb
{
GET,
POST,
PUT,
DELETE
} public class RestClient
{
#region 属性
/// <summary>
/// 端点路径
/// </summary>
public string EndPoint { get; set; } /// <summary>
/// 请求方式
/// </summary>
public EnumHttpVerb Method { get; set; } /// <summary>
/// 文本类型(1、application/json 2、txt/html)
/// </summary>
public string ContentType { get; set; } /// <summary>
/// 请求的数据(一般为JSon格式)
/// </summary>
public string PostData { get; set; }
#endregion #region 初始化
public RestClient()
{
EndPoint = "";
Method = EnumHttpVerb.GET;
ContentType = "application/json";
PostData = "";
} public RestClient(string endpoint)
{
EndPoint = endpoint;
Method = EnumHttpVerb.GET;
ContentType = "application/json";
PostData = "";
} public RestClient(string endpoint, EnumHttpVerb method)
{
EndPoint = endpoint;
Method = method;
ContentType = "application/json";
PostData = "";
} public RestClient(string endpoint, EnumHttpVerb method, string postData)
{
EndPoint = endpoint;
Method = method;
ContentType = "application/json";
PostData = postData;
}
#endregion #region 方法
/// <summary>
/// http请求(不带参数请求)
/// </summary>
/// <returns></returns>
public string HttpRequest()
{
return HttpRequest("");
} /// <summary>
/// http请求(带参数)
/// </summary>
/// <param name="parameters">parameters例如:?name=LiLei</param>
/// <returns></returns>
public string HttpRequest(string parameters)
{
var request = (HttpWebRequest)WebRequest.Create(EndPoint + parameters); request.Method = Method.ToString();
request.ContentLength = ;
request.ContentType = ContentType; if (!string.IsNullOrEmpty(PostData) && Method == EnumHttpVerb.POST)
{
var bytes = Encoding.UTF8.GetBytes(PostData);
request.ContentLength = bytes.Length; using (var writeStream = request.GetRequestStream())
{
writeStream.Write(bytes, , bytes.Length);
}
} using (var response = (HttpWebResponse)request.GetResponse())
{
var responseValue = string.Empty; if (response.StatusCode != HttpStatusCode.OK)
{
var message = string.Format("请求数据失败. 返回的 HTTP 状态码:{0}", response.StatusCode);
throw new ApplicationException(message);
} using (var responseStream = response.GetResponseStream())
{
if (responseStream != null)
using (var reader = new StreamReader(responseStream))
{
responseValue = reader.ReadToEnd();
}
}
return responseValue;
}
}
#endregion
}
}
接下来我们验证上面resful 服务端当中的两个实例,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json; namespace RestFulClient
{
class Program
{
static void Main(string[] args)
{
Console.Title = "Restful客户端Demo测试"; RestClient client = new RestClient();
client.EndPoint = @"http://127.0.0.1:7788/"; client.Method = EnumHttpVerb.GET;
string resultGet = client.HttpRequest("PersonInfoQuery/王二麻子");
Console.WriteLine("GET方式获取结果:" + resultGet); client.Method = EnumHttpVerb.POST;
Info info = new Info();
info.ID = ;
info.Name = "张三";
client.PostData = JsonConvert.SerializeObject(info);//JSon序列化我们用到第三方Newtonsoft.Json.dll
var resultPost = client.HttpRequest("PersonInfoQuery/Info");
Console.WriteLine("POST方式获取结果:" + resultPost);
Console.Read();
}
} [Serializable]
public class Info
{
public int ID { get; set; }
public string Name { get; set; }
}
}
结果如图所示:
至此、一个简单的Restful 服务端和Restful 客户端的就这样实现了,下篇文章讲解如果设计一个高效可扩展的Restful完整帮助类,这样我们就可以跨平台、跨语言访问其他平台提供的Restful Service了。
文章代码已上传Github--->下载地址
PS:如有疑问,请留言,未经允许,不得私自转载,转载请注明出处:http://www.cnblogs.com/xuliangxing/p/8735552.html
C# 服务端篇之实现RestFul Service开发(简单实用)的更多相关文章
- C# 客户端篇之实现Restful Client开发(RestSharp帮助类)
上篇文章<C# 服务端篇之实现RestFul Service开发(简单实用)>讲解到,如果开发一个简单的Restful风格的Service,也提到了简单创建一个Restful Client ...
- 基于APNs最新HTTP/2接口实现iOS的高性能消息推送(服务端篇)
1.前言 本文要分享的消息推送指的是当iOS端APP被关闭或者处于后台时,还能收到消息/信息/指令的能力. 这种在APP处于后台或关闭情况下的消息推送能力,通常在以下场景下非常有用: 1)IM即时通讯 ...
- Mina airQQ聊天 服务端篇(二)
Mina聊天服务端实现思路:在用户登录的时候.连接服务端而且验证登录用户,假设成功,则将IoSession保存到map<账号,IoSession>中,而且通知该用户的好友上线,然 后再请求 ...
- SharePoint 2013 APP 开发示例 (六)服务端跨域访问 Web Service (REST API)
上个示例(SharePoint 2013 APP 开发示例 (五)跨域访问 Web Service (REST API))是基于JavaScript,运行在web browser内去访问REST AP ...
- app渗透测试 服务端篇
基本知识 平时安装的应用位置,里面主要是odex可运行文件 /data/app 系统应用位置(需要root权限),里面主要是odex可运行文件 /system/app 应用的数据相关的位置,里面包含一 ...
- 使用Charles设置https代理到http以及证书安装(服务端篇)
1.下载ssl证书到[登录],并且设置证书[始终信任] 2.SSL Proxying设置,Location为*,可以抓全部接口的https请求 参考:https://www.jianshu.com/p ...
- 梨子带你刷burp练兵场(burp Academy) - 服务端篇 - Sql注入配套漏洞讲解笔记
目录 Sql注入 什么是Sql注入呢? Sql注入有哪些例子? 检索隐藏数据 打破应用逻辑 利用Union进行跨库查询 如何确定利用Union的注入攻击所需的列数呢? 如何确定Union的查询结果中哪 ...
- 对爱奇艺PC Web主站来说,良好的SEO能够帮助其获得更多的搜索流量,因而页面上一些非常重要的内容仍然需要依靠服务端进行渲染,由于另外开发一套基于Node的SSR后台成本较高,而乐趣(基于java和velocity模板引擎)平台作为渲染系统已经十分成熟且运行稳定,在充分试验后,我们决定在Uniqy中使用服务端同步与客户端浏览器异步二次渲染相结合的方式,结合Vue2.0提供的 slot插槽机制,很
https://mp.weixin.qq.com/s/eB20BoqzENO_oNk8eDg4Eg 干货|爱奇艺PC Web新框架实践 原创: 前端研发团队 爱奇艺技术产品团队 昨天
- 移动端(H5)弹框组件--简单--实用--不依赖jQuery
俗话说的好,框架是服务与大家的,包含的功能比较多,代码多.在现在追求速度的年代.应该根据自己的需求去封装自己所需要的组件. 下边就给大家介绍一下自己封装的一个小弹框组件,不依赖与jQuery,代码少, ...
随机推荐
- Grok patterns 汇总
S3_REQUEST_LINE (?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawreques ...
- IO流(6)—转换流
1.处理流之二:转换流 InputStreamReader和OutputStreamWriter 2.当作用的文件就是一个文本文件且使用字节流传输时,需要把它转换成字符流,再在外面加上缓冲流以加速传输 ...
- jQuery 学习02——效果:隐藏/显示、淡入淡出、滑动、动画、停止动画、Callback、链
jQuery 效果- 隐藏hide()和显示show() 语法: $(selector).hide(speed,callback);$(selector).show(speed,callback); ...
- .NET分布式缓存Memcached从入门到实战
一.课程介绍 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是 ...
- Vue.JS React 精彩文章汇总
JavaScript深入系列 [干货] JavaScript数组所有API全解密 [干货] 移动端:页面->手淘互动动效的探索 - IT大咖说 - 大咖干货,不再错过 [扫盲] Jonath ...
- 【springboot】【redis】springboot+redis实现发布订阅功能,实现redis的消息队列的功能
springboot+redis实现发布订阅功能,实现redis的消息队列的功能 参考:https://www.cnblogs.com/cx987514451/p/9529611.html 思考一个问 ...
- unity-Profiler调试Android的正确姿势(mumu模拟器)
1. 前置条件 安卓的相关环境 java.ant.sdk.ndk 什么的都装好(其实这里只需要 sdk 里面的 adb),配好 adb 工具的环境变量(意思就是 cmd 里直接输 adb 命令即可) ...
- 使用sshfs将远程目录挂载到本地
使用sshfs将远程目录挂载到本地 转自:http://blog.sina.com.cn/s/blog_6561ca8c0102vc2u.html 在Linux下我们通常使用ssh命令来登录远程Lin ...
- <climits>头文件定义的符号常量
<climits>头文件定义的符号常量 CHAR_MIN char的最小值 SCHAR_MAX signed char 最大值 SCHAR_MIN signed char 最小值 ...
- 关于VC预定义常量_WIN32,WIN32,_WIN64
VC2012 下写 Windows 程序时,有时需要判断编译环境.在之前的文章<判断程序是否运行在 Windows x64 系统下.>里说过如何在运行期间判断系统环境,但在编译时如何判断? ...