.NET REST 服务






4.98/5 (15投票s)
自安装的版本跟踪 REST 服务,用于构建
引言
这个项目虽然小,但雄心勃勃;
- REST Web 服务 (套接字监听器/HTTP 解析器)
- 自安装的 Windows 服务
- 12.5 KB
- 做了一些有用的事情 (获取/设置/递增/持久化版本号)
背景
我最初的目的是创建一个服务来集中管理构建版本号,但最终却成为一个关于 .NET 中轻量级 RESTful 服务实现的有趣研究。在构建环境中,版本递增通常通过手动编写脚本、自定义操作或工作流活动来解决。这通常不是一个构建解决方案中最优雅的部分。本项目构建了一个更优雅的 REST 服务,它直接从构建过程中调用 RESTful 服务,该服务直接递增和获取任意数量的构建版本。例如,打开你的网页浏览器并输入 URL
https://:2313/LodeRunner?v=+
..将返回
3.0.0.1
你最喜欢的 LodeRunner 仿制项目构建的版本。
自安装
这项服务的自安装功能实际上源自这篇文章... 非常感谢。关键在于你需要定义一个服务和一个安装程序,然后根据需要处理传入的参数 (参见Versioner.Install.cs 和 Versioner.Services.cs)。
安装程序
[RunInstaller(true)]
public class Installer : inst.Installer
{
public Installer()
{
Installers.Add(new ServiceProcessInstaller() { Account = ServiceAccount.LocalSystem });
Installers.Add(new ServiceInstaller()
{
DelayedAutoStart = false,
StartType = ServiceStartMode.Automatic,
Description = "Tracks version numbers for build resources",
ServiceName = Service.Name,
DisplayName = "Versioner Service",
});
}
}
这是服务管理器所必需的,以便提供你从服务管理器中看到的那些基本信息。服务本身在 Versioner.Service
中。这实际上就是服务的“主入口”。稍后在讨论它是如何工作的时,我们会详细讨论它。
SimplyListen
正确设置套接字有很多细节,所以我创建了一个助手。该类封装了监听的基本概念,并在套接字连接时提供一个 Action<T>
回调。
public class SimplyListen : IDisposable
{
private Action<Socket> _onConnect;
private SimplyListen Listen(int port, Action<Socket> onConnect)
{
_onConnect = onConnect;
// Create the listener socket in this machines IP address
_socketLast = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
_socketLast.Bind(new IPEndPoint(LocalHost, port));
//listener.Bind( new IPEndPoint( IPAddress.Loopback, 399 ) );
// For use with localhost 127.0.0.1
_socketLast.Listen(10);
// Setup a callback to be notified of connection requests
_socketLast.BeginAccept(new AsyncCallback(OnConnectRequest), _socketLast);
return this;
}
...
}
这里的关键调用是 OnConnectRequest
。这是套接字层在需要响应传入套接字连接时调用的回调方法。理解这段代码的关键在于认识到有两个套接字...一个是“监听”的、一直保持打开的套接字,另一个是客户端连接的、你可以接收传入字节的套接字。_socketLast.EndAcccept()
调用获取内部套接字并将其传递给我们的 _onConnect Action<T>
。从那里,调用者可以只处理连接方面的问题,而无需处理套接字的所有其他复杂性。
private Socket _socketLast = null;
private void OnConnectRequest(IAsyncResult ar)
{
lock (_socketLast)
{
_socketLast = ar.AsyncState as Socket;
}
_onConnect(_socketLast.EndAccept(ar));
_socketLast.BeginAccept(new AsyncCallback(OnConnectRequest), _socketLast);
}
版本控制是啥?!
所以所有这些“装饰”都围绕着真正的代码,也就是持久化版本跟踪机制。这个想法是我们在磁盘上存储一个逗号分隔的版本字符串列表。它看起来像这样:
bruce,1.0.3.1
bob,3.0.0.3
LoadRunner,1.0.0.1
我使用一个非常基础的 LINQ 命令将其转换为一个 Dictionary,以便在内存中方便地使用...
Versions = !File.Exists (filename) ? new Dictionary<string, string>() :
File.ReadAllLines(filename).Select(l =>
l.Split(',')).Where(v=>v.Length==2).ToDictionary(k => k[0], v => v[1])
保存它也使用了类似的 LINQ 命令。
File.WriteAllLines(filename,
Versions.Select(v=>string.Format("{0},{1}", v.Key, v.Value)).ToArray());
幕后工作:将所有内容整合在一起
URL 参数中的命令可以是你想设置的版本,或者一个或多个加号 ('+'),这意味着“递增”版本号。加号的数量表示要递增的版本号的位置。一个加号表示递增最后一个数字,两个表示递增倒数第二个,依此类推。我将其编码为,版本号中的分隔符数量没有限制,4 是标准的,但我特意让实现更加健壮,以便任何数量都可以正常工作。
private static string Increment(string name, string version)
{
string response = version;
if (!string.IsNullOrEmpty(version))
{
int plusCount = version.Count(c => '+' == c);
if (plusCount > 0)
{
if (!vers.Versions.TryGetValue(name, out response))
response = "1.0.0.0";
response = Versioner.Increment(response, plusCount);
}
}
return response;
}
这使用了 Versioner
中的一个静态方法来执行实际的数字递增。它将版本字符串分割成一个数组,解析为整数,在正确的位置递增,然后重新连接。
public static string Increment(string response, int incIndex)
{
var ints = response.Split('.').Select(i => int.Parse(i)).ToArray();
int pos = ints.Length - incIndex;
if (pos < 0)
return response;
ints[pos] = ints[pos]+1;
while (++pos < ints.Length)
ints[pos] = 0;
return string.Join(".", ints.Select(i => i.ToString()));
}
如果我们已经确定了我们的版本号,现在我们需要更新内存中的字典并保存它。此代码还处理我们只是请求版本号的情况 (根本没有版本参数)。那就是“TryGet
”。
private static string UpdateVersions(string name, string response)
{
if (!string.IsNullOrEmpty(response))
{
if (vers.Versions.ContainsKey(name))
vers.Versions[name] = response;
else
vers.Versions.Add(name, response);
vers.Save(_versionFilename);
}
else if (!vers.Versions.TryGetValue(name, out response))
{
response = string.Empty;
}
return response;
}
最后,这是将所有内容包装在一起的代码。它位于响应连接尝试的服务处理程序的 lambda 中。
ReadHttpRequestBuffer()
从套接字读取并返回名称和版本参数。Increment()
根据版本参数进行请求。UpdateVersions()
更新内存中的字典并将版本持久化到磁盘。- 将响应作为字节数组发送。
string name, version;
if (SimplyListen.ReadHttpRequestBuffer(s, out name, out version))
{
var response = UpdateVersions(name, Increment(name, version));
// Convert to byte array and send.
var byteDateLine = System.Text.Encoding.ASCII.GetBytes(response);
s.Send(byteDateLine, byteDateLine.Length, 0);
}
使用方法
安装
- 复制到你想存放的位置。
- 以 **管理员身份** 启动命令提示符。
- 切换到复制的目录。
- 运行...
Versioner.exe /i
Running a transacted installation.
Beginning the Install phase of the installation.
.
.
安装完成后,你可以打开任何浏览器并输入此内容:
https://:2313/LodeRunner?v=1.0.0.0
1.0.0.0
https://:2313/LodeRunner?v=++
1.0.1.0
URL 的末尾是项目的名称,'v' 参数有两种模式:
- 如果指定一个由点分隔的整数列表,它将设置该实体的版本号。
- 如果指定任意数量的 '+' 字符,它将设置版本号中的相应索引...
示例:'v=+' 递增 (并保存) "构建" 数字,'v=+++' 递增次要版本号。
未来:在实际构建中使用
你需要自己实现如何将这个功能集成到你的构建中,但在接下来的几周里,我可能会在这篇文章的第二部分中更新 TFS 构建工作流中的构建号。
历史
- 12 年 5 月 17 日 - 创建
- 12 年 5 月 19 日 - 重写了引言,使其更具普遍性
- 12 年 5 月 25 日 - 修复了本地主机中的一个 bug