让Web API支持Protocol Buffers


简介

现在我们Web API项目基本上都是使用的Json作为通信的格式,随着移动互联网的兴起,Web API不仅其他系统可以使用,手机端也可以使用,但是手机端也有相对特殊的地方,网络通信除了wifi,还有蜂窝网络比如2G/3G,当手机处于这种网络环境下并且在一些偏僻或者有建筑物阻挡的地方,网络会变得非常差,之前我有测试过ProtoBuf和Json在序列化和反序列化上性能的对比,通过测试发现ProtoBuf在序列化和反序列化以及序列化后字节的长度和其他的框架相比都有很大的优势,所以这篇文章准备让Web API也支持Protocol Buffers。

实现

要让Web API支持ProtoBuf就需在Web API的HttpConfigurationFormatters中注册MediaTypeFormatter,这里已经有现成的解决方案,我们直接使用。

  • 从nuget上下载WebApiContrib.Formatting.ProtoBuf.
  • 修改配置,新增config.Formatters.Add(new ProtoBufFormatter());

    public static void Register(HttpConfiguration config)
    {
        // Web API 配置和服务
    
        // Web API 路由
        config.MapHttpAttributeRoutes();
    
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",   //修改路由规则
            defaults: new { id = RouteParameter.Optional }
        );
    
        config.Formatters.Add(new ProtoBufFormatter());
    }
    

完成上面两步, 我们的Web API就已经支持了ProtoBuf,我们可以新建一个Controller来进行测试。

public class UserController : ApiController
{

    /// <summary>
    /// 注册用户
    /// </summary>
    /// <param name="userDto"></param>
    /// <returns></returns>
    public string Regist(UserDto userDto)
    {
        if (!string.IsNullOrEmpty(userDto.UserName) && !string.IsNullOrEmpty(userDto.Password))
        {
            return "regist success";
        }
        return "regis error";
    }

    //登陆
    public string Login(UserLoginDto userLoginDto)
    {
        if (userLoginDto.UserName == "sa")
        {
            return "login success";
        }
        return "loign fail";
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="id"></param>
    /// <param name="userName"></param>
    /// <returns></returns>
    public UserDto Get(string userName)
    {
        if (!string.IsNullOrEmpty(userName))
        {
            return new UserDto()
            {
                UserName = "sa",
                Password = "123",
                Subscription = new List<string>() {"news", "picture"}
            };
        }
        return null;
    }
}

这里对实体也要进行修改。

[ProtoContract]
public class UserDto
{
    [ProtoMember(1)]
    public string UserName { get; set; }

    [ProtoMember(2)]
    public string Password { get; set; }

    [ProtoMember(3)]
    public List<string> Subscription { get; set; }
}

[ProtoContract] public class UserLoginDto { [ProtoMember(1)] public string UserName { get; set; }

    [ProtoMember(2)]
    public string Password { get; set; }
}

新建一个测试客户端,控制台就可以,引用WebApiContrib.Formatting.ProtoBuf和Web Api Client

将我们刚才创建的Dto复制到客户端,测试代码如下:

static void Main(string[] args)
    {
        var client = new HttpClient { BaseAddress = new Uri("http://192.168.16.9:8090/") };
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf"));

        Regist(client);
        Console.WriteLine("===================================");
        Login(client);
        Console.WriteLine("===================================");
        GetUser(client);
        Console.WriteLine("===================================");
        Console.ReadLine();
    }

    private static void Regist(HttpClient client)
    {
        //注册
        var userDto = new UserDto()
        {
            UserName = "sa",
            Password = "123456",
            Subscription = new List<string>() { "news", "picture" }
        };

        HttpResponseMessage response = client.PostAsync("api/User/Regist", userDto, new ProtoBufFormatter()).Result;

        if (response.IsSuccessStatusCode)
        {
            var p = response.Content.ReadAsAsync<string>(new[] { new ProtoBufFormatter() }).Result;
            Console.WriteLine(p);
        }
    }

    private static void Login(HttpClient client)
    {

        //登陆
        var userlogin = new UserLoginDto()
        {
            UserName = "sa",
            Password = "123456",
        };

        HttpResponseMessage response = client.PostAsync("api/User/login", userlogin, new ProtoBufFormatter()).Result;
        if (response.IsSuccessStatusCode)
        {
            var p = response.Content.ReadAsAsync<string>(new[] { new ProtoBufFormatter() }).Result;
            Console.WriteLine(p);
        }
    }

    private static void GetUser(HttpClient client)
    {

        HttpResponseMessage response = client.GetAsync("api/User/Get?userName=sa").Result;

        if (response.IsSuccessStatusCode)
        {
            var p = response.Content.ReadAsAsync<UserDto>(new[] { new ProtoBufFormatter() }).Result;

            Console.WriteLine(p.UserName + " " + p.Password);
            foreach (var s in p.Subscription)
            {
                Console.WriteLine(s);
            }
        }
    }

运行结果如下:

如上我们已经可以看到输出成功

知识共享许可协议
《让Web API支持Protocol Buffers》 常伟华 创作。
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议 | 3.0 中国大陆许可协议进行许可。

站内公告