前言

WCF作为通迅框架可以很容易地实现对消息的压缩,且方法不止一种,主要解决方法主要有以下四种:

1、通过自定义MessageEncoder和MessageEncodingBindingElement 来完成。具体的实现,可以参阅张玉彬的文章《WCF进阶:将编码后的字节流压缩传输》;

2、直接创建用于压缩和解压缩的信道,在CodePlex中具有这么一个WCF Extensions

3、自定义MessageFormatter实现序列化后的压缩和反序列化前的解压,详见WCF大师Artech中的博客有《通过WCF扩展实现消息压缩》;

4、自定义MessageInspector实现,详见博客园似若流云的文章《WCF 消息压缩性能问题及解决方法》。

这几种方法实现、配置都很简单。后两种方法的内部实现方法很类似,区别在于第三种方法通过自定义MessageFormatter中对消息进行压缩和解压缩,而第四种方法是在自定义MessageInspector中对消息进行压缩和解压缩。比较而言最后一种是最简单粗暴的。

几种方案的适用场景

那么,这几种方法都适用于什么场景呢?从技术上看,这4种方案基本可以分为两类。一种是在消息编码器上动手脚,另一种是在消息上做文章。

第一种是属于在消息编码器上动手脚的,实现稍复杂。应用场景比较广泛,基本上所有的场景都是适用的。

后面三种都是在消息上做文章的,只适合有WCF客户端的情况,因为如果没有客户端压缩时在消息中加入的压缩标志,服务端就没法正确解压,反之亦然。虽然原理相同,但三种方法的切入点各不相同。同时,第二种方法是可以改成在消息编码器上进行压缩/解压的。

因为RESTFul的WCF的客户端不仅仅是WCF,所以暂时只能选第一种方案了。

一个问题

虽然有现成的方案可用,但是,如果要完美支持多种客户端的话,这里面还有几个问题需要解决。

按照Http协议的规范,客户端发送/服务端返回一个压缩的数据,需要在协议头部加上Content-Encoding,并设置其值为gzip或者deflate。告诉对方数据的压缩方法,好让对方能够正确解压。

如果客户端希望返回的数据是压缩的,那么就在头部加上Accept-Encoding,并设置其值为gzip或者deflate服务器收到这个信息后,就知道客户端选择的压缩方法,就可以按照客户端指定的方法去压缩数据。

然而,无论哪种方案,都是需要事先配置压缩方式的。也就是说,需要双方事前约定,无法实现用Content-Encoding的值来告知对方压缩方式!这不优雅!!在很多时候,这是个大问题!!!

解决的办法

我们知道,第四种方案的切入点在消息检查器上,在这个点上,通常会实现一些自定义的拦截功能。一个自定义的消息检查器需要继承IDispatchMessageInspector,这个接口类定义了两个接口:

object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
void BeforeSendReply(ref Message reply, object correlationState)

AfterReceiveRequest作用在收到消息后,BeforeSendReply作用在发送响应消息前。

我们可以通过下面的代码,来根据请求头的Accept-Encoding的值,给消息加上对应的压缩标示,以便消息编码器选择正确的压缩方式;并在返回响应前在响应头部加上Content-Encoding并设置相应的值,以便客户端正确解压。

using System.Linq;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
namespace Insight.WCF.CustomEncoder
{
public class CompressInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
var property = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
var accept = property?.Headers[HttpRequestHeader.AcceptEncoding];
switch (accept)
{
case "gzip":
OperationContext.Current.Extensions.Add(new GzipExtension());
break;
case "deflate":
OperationContext.Current.Extensions.Add(new DeflateExtension());
break;
}
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
var property = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
var exts = OperationContext.Current.Extensions;
if (exts.OfType(GzipExtension).Any())
{
property?.Headers.Add(HttpResponseHeader.ContentEncoding, "gzip");
}
else if (exts.OfType(DeflateExtension).Any())
{
property?.Headers.Add(HttpResponseHeader.ContentEncoding, "deflate");
}
}
}
public class GzipExtension : IExtension
{
public void Attach(OperationContext owner)
{
}
public void Detach(OperationContext owner)
{
}
}
public class DeflateExtension : IExtension
{
public void Attach(OperationContext owner)
{
}
public void Detach(OperationContext owner)
{
}
}
}

未彻底解决的问题

因为消息检查器的AfterReceiveRequest作用在收到消息后,也就是说,对于POST/PUT/DELETE这三类请求,它们如果对提交的数据进行了压缩的话,我们无法在消息编码器中根据Content-Encoding的值进行解压。消息编码器中的ReadMessage方法是这样的:

public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)

所以,如果要实现由客户端来决定POST/PUT/DELETE数据的压缩方式的话,只有在Content-Type上面搞点小动作。

这。。。。。。

不够优雅呀!

看来得研究下改造第二种解决方案了。生命不止,折腾不息

源代码在这里:https://github.com/xuanbg/Utility/tree/master/CustomEncoder

基于WCF的RESTFul WebAPI如何对传输内容实现压缩的更多相关文章

  1. HTTP 传输内容的压缩

    一.HTTP压缩和内容编码的区别 HTTP压缩,在HTTP协议中,其实是内容编码的一种. 在http协议中,可以对内容(也就是body部分)进行编码, 可以采用gzip这样的编码. 从而达到压缩的目的 ...

  2. HTTP传输内容的压缩

    最近在看尤大的ssr项目的demo,看他的项目里有用到compression,完全看不懂这是什么鬼,然后百度了一下,文档也都是英文的,看着有点吃力,隐约的觉得这是压缩http请求的,做前端的都知道,在 ...

  3. 基于WCF的支持跨局域网可断点续传的大文件传输服务实现

    题外话:这个系列的文章记录了本人最近写的一个小工程,主要包含了两个功能,一是对文件的断点续传的功能,二是基于WCF的一对多文件主动发送的功能,顺便这也是我自己在WCF学习路上的一个小成果吧. 在网上找 ...

  4. Restful WebApi项目开发实践

    前言 踩过了一段时间的坑,现总结一下,与大家分享,愿与大家一起讨论. Restful WebApi特点 WebApi相较于Asp.Net MVC/WebForm开发的特点就是前后端完全分离,后端使用W ...

  5. 构建一个基于 Spring 的 RESTful Web Service

    本文详细介绍了基于Spring创建一个“hello world” RESTful web service工程的步骤. 目标 构建一个service,接收如下HTTP GET请求: http://loc ...

  6. 基于WCF 的远程数据库服务访问技术

    原文出处:http://www.lw80.cn/shuji/jsjlw/13588Htm.Htm摘要:本文介绍了使用WCF 建立和运行面向服务(SOA)的数据库服务的系统结构和技术要素,分析了WCF ...

  7. Python flask 基于 Flask 提供 RESTful Web 服务

    转载自 http://python.jobbole.com/87118/ 什么是 REST REST 全称是 Representational State Transfer,翻译成中文是『表现层状态转 ...

  8. 用C#基于WCF创建TCP的Service供Client端调用

    本文将详细讲解用C#基于WCF创建TCP的Service供Client端调用的详细过程 1):首先创建一个Windows Service的工程 2):生成的代码工程结构如下所示 3):我们将Servi ...

  9. Restful WebApi开发实践

    随笔分类 - Restful WebApi开发实践   C#对WebApi数据操作 摘要: ## 目标简化并统一程序获取WebApi对应实体数据的过程,方便对实体进行扩充.原理就是数据服务使用反射发现 ...

随机推荐

  1. SQL点滴3—一个简单的字符串分割函数

    原文:SQL点滴3-一个简单的字符串分割函数 偶然在电脑里看到以前保存的这个函数,是将一个单独字符串切分成一组字符串,这里分隔符是英文逗号“,”  遇到其他情况只要稍加修改就好了 CREATE FUN ...

  2. WP 开发中.xaml 与.xaml.cs

    关于 WP 开发中.xaml 与.xaml.cs 的关系   今天我们先来看一下在WP8.1开发中最长见到的几个文件之间的关系.比较论证,在看这个问题之前我们简单看看.NET平台其他两个不同的框架: ...

  3. leetcode第26题--Remove Duplicates from Sorted Array

    problem: Given a sorted array, remove the duplicates in place such that each element appear only onc ...

  4. 条件变量signal与unlock的顺序

    编写同步队列时,有用到条件变量,对操作队列的线程进行同步.当队列为空时,允许get线程挂起,直到add线程向队列添加元素并通过唤醒条件变量,get线程继续向下运行.条件变量在多线程程序中用来实现“等待 ...

  5. 通过改变viewport 实现网站自适应

    var phoneWidth = parseInt(window.screen.width); var phoneScale = phoneWidth/640; var userAgent = nav ...

  6. MEF简介及简单的Demo

    MEF简介及简单的Demo 文章开始之前,首先简单介绍一下什么是MEF,MEF,全称Managed Extensibility Framework(托管可扩展框架).单从名字我们不难发现:MEF是专门 ...

  7. Qt的SQL操作,DELETE,SELECT

    #include <QObject> #include <QSqlDatabase> #include <QSqlError> #include <QSqlQ ...

  8. RikMigrations 或 Migrator.NET 进行自动化的数据库升级

    一种版本化的数据库脚本管理机制 现今开发的软件当中,多数系统的数据都是基于数据库存储的,但是由于软件变化的复杂性,相对于维护代码,数据库架构的版本并不是那么好维护. 这里本人针对实际情况,理想化出一种 ...

  9. c# in deep 之使用匿名方法的内联委托操作

    匿名方法允许我们指定一个内联委托的操作,为创建委托实例表达式的一部分.其可以对代码进行极度精简,当然可读性变得很差.下面看一个求平方根的例子. List<int> list = new L ...

  10. twisted学习笔记 No.1

    原创博文,转载请注明出处 . 1.安装twisted ,然后安装PyOpenSSL(一个Python开源OpenSSL库),这个软件包用于给Twisted提供加密传输支持(SSL).最后,安装PyCr ...