在 MVC Controller 中,不依靠定制路由,随心所欲地拦截请求和处理请求,想如何输入输出都

本文讲解"在 MVC Controller 中,不依靠定制路由,随心所欲地拦截请求和处理请求,想如何输入输出都",用于解决相关问题。

  Asp.net 中的 MVC,其主要面向 “text” 类型的 Content-Type 来处理 HTTP 请求,除了文件传输之外,就连 json、xml 也都是文本类型。
因此,对于 text 类型的输入输出,MVC 自然处理得很好。可有时候,这并不能令我们满意。
当我们要传输二进制 byte[] 数组、序列化结构数据、以及任何特殊请求的处理时,该怎么办呢?难道非要将它们以 base64 编码,抑或是为特殊请求定制一个路由,譬如转到 IHttpHandler/IRouteHandler 去处理吗?
我也是一个 Asp.net 的初学者,更是一个 MVC 的初学者,刚开始我也跟着这个思路去做,结果发现越增加路由,就会给程序带来意想不到的麻烦。我就加了一句话: routes.Add( "myRouteName", new Route( "ac/aucenter", new MyHttpRouteHandler() ) ); 就引起视图页面的所有链接,全都变成了 http://host/ac/aucenter?,而为这些链接指定的控制器,Action,全都变成了该 Url 的参数。
当然,我可以不增加这个路由,而是将 “加密后的二进制数据” 进行 base64 编码后再传输,可是,这在一个 C++ 客户端 DLL 中,我又怎么能做好 base64 编码呢?在 C++ 中,与 asp.net 服务端进行交互非常困难,直接发送和接收二进制、序列化数据最简单。反正,我一定不要增加路由,在路由上想任何办法,都是 “打补丁” 的思维方式。
本着我对 C++ 和 Windows 十多年的经验,我猜想 MVC 一定能让我任意地拦截 HTTP 请求,不让它进入系统的路由中逗一圈,而是在进入路由之前,让我捕获,以转入我自己的处理。但是在网上找了半天也没找到如意的,有说加 Filter 的,有说处理 404 错误的,方法非常多,但都有这样那样的问题。 譬如说,加 Filter 吧,我既然都不调用 routes.Add() ,那么加 Filter 没用啊;处理 404? 这得要全局拦截,在 Application 中拦截 404,你放心吗?还居然有 server.GetLastError() 的调用都来了,这明显是取服务器最后一个错误啊,那么多错误同时发生,你就拦截最后一个错误?
在路由上,我实在找不到办法,于是就转入 Controller 本身,看看它能否直接处理二进制数据,特别关注 Controller 是怎么把响应数据发回给客户端的,ContentResult,JsonResult,FileStreamResult 等等,都是用来发回响应的,我于是翻看了下 ContentResult 的 .NET 源代码,然后恍然大悟。
public class ContentResult : ActionResult
{  public override void ExecuteResult(ControllerContext context)  {      if( context == null )          throw new ArgumentNullException("context");     HttpResponseBase response = context.HttpContext.Response;      if( !string.IsNullOrEmpty( this.ContentType ) )          response.ContentType = this.ContentType;     if( this.ContentEncoding != null )          response.ContentEncoding = this.ContentEncoding;     if( this.Content != null )          response.Write(this.Content);  } public string Content { get; set; } public Encoding ContentEncoding { get; set; } public string ContentType { get; set; }
}
  在 ContentResult 中,它的 ExecuteResult() 不就是调用 HttpContext.Response.Write() 把数据写入 response,发回客户端的吗。只是,ExecuteResult() 不是由我们直接调用的,而是在我们的 Controller 的 Action() 处理结束返回该 ContentResult 对象后,由 MVC 接着调用的。
为啥非要让 MVC 调用 Response.Write(),我直接调用不也可以吗?我调用完之后,返回一个 EmptyResult,让它啥也不干,不就好了吗?
再翻看 EmptyResult 的 .NET 源码发现,它确实啥也不干,空空如也。其内部只实现了一个单例模型。
再来看看 Request 请求,我测试后发现,服务器端确实能接收到二进制流,如下所示:
public class TestController : Controller
{  [HttpPost]  public ActionResult Index()  {      MemoryStream inStream = new MemStream( Math.Max( Request.TotalBytes, 64 ) );      Request.InputStream.CopyTo( inStream );     // 对 inStream 做处理,将响应数据放在 rspData 中,然后:     Response.OutputStream.Write( rspData, 0, rspData.Length );     return EmptyResult.Instance;  }
}
  就这么简单,你想怎样处理 HTTP 的流进流出,都随你的意。
你也可以从 ActionResult 派生一个自己的类,例如 MyResult,在其 ExecuteResult() 接口实现中,参照 ContentResult 的代码,把响应数据 rspData 发回。而在上面的 TestController.Index() 返回你的 new MyResult( rspData ).

 

  接下来,我们来看看,各种编程语言的互操作问题,大家都赞同以 Json,Xml 来序列化数据,然后在各个平台,各种语言,各个操作系统之间交互,以我看,这并不一定简单易用。来看看我提供的一个序列化工具,简单易用,内存占用少,稍作修改就可以跨平台。
这个工具,提供 3 个类和一个接口,就是抄的 C# 的 MemoryStream,BinaryWriter,BinaryReader 的源码来编写。C# 的实现上,有诸多的小问题,例如,它无法处理 null 字符串,而这个工具能序列化 null 字符串。此外,C# 的这些类,搞了一堆没啥用的 Dispose(),有时候莫名其妙地,其内部的 Stream 就被 Dispose() 了。而这个工具,不用理会这些,它只隐含继承基类的 Dispose()。谁创建的 Stream,就由谁负责 Dispose(),否则就乱套了。
internal class BinWriter  {      Stream   stream;      Encoding encoding;      byte[]   _buffer;     internal BinWriter( Stream stream, Encoding encoding )      {          this.stream = stream;          this.encoding = encoding;          _buffer = new byte[8];      }     internal void Write( bool value )      {          stream.WriteByte( value ? ((byte)1) : ((byte) 0) );      }     internal void Write( byte value )      {          stream.WriteByte( value );      }     internal void Write( short value )      {          _buffer[0] = (byte) value;          _buffer[1] = (byte) (value >> 8);          stream.Write( _buffer, 0, 2 );      }     internal void Write( int value )      {          _buffer[0] = (byte) value;          _buffer[1] = (byte) (value >> 8);          _buffer[2] = (byte) (value >> 0x10);          _buffer[3] = (byte) (value >> 0x18);          stream.Write( _buffer, 0, 4 );      }     internal void Write( long value )      {          _buffer[0] = (byte) value;          _buffer[1] = (byte) (value >> 8);          _buffer[2] = (byte) (value >> 0x10);          _buffer[3] = (byte) (value >> 0x18);          _buffer[4] = (byte) (value >> 0x20);          _buffer[5] = (byte) (value >> 40);          _buffer[6] = (byte) (value >> 0x30);          _buffer[7] = (byte) (value >> 0x38);          stream.Write( _buffer, 0, 8 );      }     internal void Write( string value )      {          if( value == null )              Write7BitEncodedInt( stream, Int32.MinValue );          else if( value.Length == 0 )              Write7BitEncodedInt( stream, 0 );          else          {              byte[] bstr = encoding.GetBytes( value );              Write7BitEncodedInt( stream, bstr.Length );              stream.Write( bstr, 0, bstr.Length );          }      }     internal void Write( ushort value )      {          _buffer[0] = (byte) value;          _buffer[1] = (byte) (value >> 8);          stream.Write( _buffer, 0, 2 );      }     internal void Write( uint value )      {          _buffer[0] = (byte) value;          _buffer[1] = (byte) (value >> 8);          _buffer[2] = (byte) (value >> 0x10);          _buffer[3] = (byte) (value >> 0x18);          stream.Write( _buffer, 0, 4 );      }     internal void Write( ulong value )      {          _buffer[0] = (byte) value;          _buffer[1] = (byte) (value >> 8);          _buffer[2] = (byte) (value >> 0x10);          _buffer[3] = (byte) (value >> 0x18);          _buffer[4] = (byte) (value >> 0x20);          _buffer[5] = (byte) (value >> 40);          _buffer[6] = (byte) (value >> 0x30);          _buffer[7] = (byte) (value >> 0x38);          stream.Write( _buffer, 0, 8 );      }     internal void Write( DateTime value )      {          Write( value.ToBinary() );      }     internal void Write( byte[] buffer, int index, int count )      {          stream.Write( buffer, index, count );      }     internal static void Write7BitEncodedInt( Stream strm, int value )      {          uint num = (uint)value;          while( num >= 0x80 )          {              strm.WriteByte( (byte) (num | 0x80) );              num = num >> 7;          }          strm.WriteByte( (byte)num );      }  } internal class BinReader  {      Stream   stream;      Encoding encoding;      byte[]   _buffer;     internal BinReader( Stream stream, Encoding encoding )      {          this.stream = stream;          this.encoding = encoding;          _buffer = new byte[128];      }     internal int Read( byte[] buffer, int index, int count )      {          return stream.Read( buffer, index, count );      }     internal bool ReadBool()      {          return stream.ReadByte() > 0;      }     internal byte ReadByte()      {          return (byte)stream.ReadByte();      }     internal short ReadInt16()      {          stream.Read( _buffer, 0, 2 );          return (short)(_buffer[0] | (_buffer[1] << 8));      }     internal int ReadInt32()      {          stream.Read( _buffer, 0, 4 );          return (((_buffer[0] | (_buffer[1] << 8)) | (_buffer[2] << 0x10)) | (_buffer[3] << 0x18));      }     internal long ReadInt64()      {          stream.Read( _buffer, 0, 8 );          uint num = (uint) (((_buffer[0] | (_buffer[1] << 8)) | (_buffer[2] << 0x10)) | (_buffer[3] << 0x18));          ulong num2 = (ulong) (((_buffer[4] | (_buffer[5] << 8)) | (_buffer[6] << 0x10)) | (_buffer[7] << 0x18));          return (long) ((num2 << 0x20) | num);      }     internal string ReadString()      {          int capacity = Read7BitEncodedInt( stream );          if( capacity == Int32.MinValue )              return null;          else if( capacity == 0 )              return "";         MemStream memStr = stream as MemStream;          if( memStr == null )          {              if( _buffer.Length < capacity )                  _buffer = new byte[capacity];             stream.Read( _buffer, 0, capacity );              return encoding.GetString( _buffer, 0, capacity );          }          else          {              string str = encoding.GetString( memStr.GetBuffer(), (int)memStr.Position, capacity );              memStr.Seek( capacity, SeekOrigin.Current );              return str;          }      }     internal ushort ReadUInt16()      {          stream.Read( _buffer, 0, 2 );          return (ushort) (_buffer[0] | (_buffer[1] << 8));      }     internal uint ReadUInt32()      {          stream.Read( _buffer, 0, 4 );          return (uint) (((_buffer[0] | (_buffer[1] << 8)) | (_buffer[2] << 0x10)) | (_buffer[3] << 0x18));      }     internal ulong ReadUInt64()      {          stream.Read( _buffer, 0, 8 );          uint num = (uint) (((_buffer[0] | (_buffer[1] << 8)) | (_buffer[2] << 0x10)) | (_buffer[3] << 0x18));          ulong num2 = (ulong) (((_buffer[4] | (_buffer[5] << 8)) | (_buffer[6] << 0x10)) | (_buffer[7] << 0x18));          return ((num2 << 0x20) | num);      }     internal DateTime ReadDate()      {          return DateTime.FromBinary( ReadInt64() );      }     internal static int Read7BitEncodedInt( Stream strm )      {          byte num3;          int num = 0, num2 = 0;          do          {              if( num2 == 0x23 )                  throw new FormatException( "Stream Format Error" );              num3 = (byte)strm.ReadByte();              num |= (num3 & 0x7f) << num2;              num2 += 7;          }          while( (num3 & 0x80) != 0 );          return num;      }  } internal class MemStream : Stream  {      byte[] _buffer;      int _capacity, _length;      int _origin, _position;     internal MemStream() : this(0)      {      }     internal MemStream( int capacity )      {          if( capacity > 0 )              _buffer = new byte[capacity];          _capacity = capacity;      }     internal MemStream( byte[] buffer )      {          SetBuffer( buffer );      }     internal MemStream( byte[] buffer, int index, int count )      {          SetBuffer( buffer, index, count );      }     internal void SetBuffer( byte[] buffer )      {          _buffer = buffer;          _length = _capacity = buffer.Length;          _origin = _position = 0;      }     internal void SetBuffer( byte[] buffer, int index, int count )      {          _buffer = buffer;          _length = _capacity = index + count;          _origin = _position = index;      }     internal bool EnsureCapacity( int value )      {          if( value <= _capacity )              return false;         int num = value;          if( num < 0x100 )              num = 0x100;         if( num < _capacity * 2 )              num = _capacity * 2;          if( _capacity * 2 > 0x7fffffc7 )              num = (value > 0x7fffffc7) ? value : 0x7fffffc7;          Capacity = num;          return true;      }     public override void Flush()      {      }     public virtual byte[] GetBuffer()      {          return _buffer;      }     internal byte[] ToBytesArray()      {          int    memLen = (int)this.Length;          byte[] aryBytes = new byte[memLen];          if( memLen > 0 )              Array.Copy( _buffer, _origin, aryBytes, 0, memLen );          return aryBytes;      }     /// <summary>      /// 从当前流读取字节序列,并将此流中的位置提升读取的字节数。      /// </summary>      /// <param name="offset">buffer 中的从零开始的字节偏移量,从此处开始存储从当前流中读取的数据。</param>      /// <param name="count">要从当前流中最多读取的字节数</param>      /// <returns>读入缓冲区中的总字节数。如果当前可用的字节数没有请求的字节数那么多,则总字节数可能小于请求的字节数;如果已到达流的末尾,则为零 (0)。</returns>      public override int Read( byte[] buffer, int offset, int count )      {          int byteCount = _length - _position;          if( byteCount > count )              byteCount = count;          if( byteCount <= 0 )              return 0;          if( byteCount <= 8 )          {              int num2 = byteCount;              while( --num2 >= 0 )                  buffer[offset + num2] = _buffer[_position + num2];          }          else              Array.Copy( _buffer, _position, buffer, offset, byteCount );          _position += byteCount;          return byteCount;      }     public override int ReadByte()      {          if( _position >= _length )              return -1;          return _buffer[_position++];      }     public override long Seek( long offset, SeekOrigin loc )      {          switch( loc )          {              case SeekOrigin.Begin:              {                  int num = _origin + (int)offset;                  if( offset < 0L || num < _origin )                      throw new IOException( "IO.IO_SeekBeforeBegin" );                  _position = num;                  break;              }              case SeekOrigin.Current:              {                  int num2 = _position + (int)offset;                  if( (_position + offset) < _origin || num2 < _origin )                      throw new IOException( "IO.IO_SeekBeforeBegin" );                  _position = num2;                  break;              }              case SeekOrigin.End:              {                  int num3 = _length + (int)offset;                  if( (_length + offset) < _origin || num3 < _origin )                      throw new IOException( "IO.IO_SeekBeforeBegin" );                  _position = num3;                  break;              }              default:                  throw new ArgumentException( "Argument_InvalidSeekOrigin" );          }          return (long)_position;      }     public override void SetLength( long value )      {          if( value > 0x7fffffff - _origin )              throw new ArgumentOutOfRangeException( "value", "ArgumentOutOfRange_StreamLength" );          int num = _origin + (int)value;          if( !EnsureCapacity( num ) && num > _length )              Array.Clear( _buffer, _length, num - _length );         _length = num;          if( _position > num )              _position = num;      }     public override void Write( byte[] buffer, int offset, int count )      {          int num = _position + count;          if( num > _length )          {              bool flag = _position > _length;              if( num > _capacity && EnsureCapacity( num ) )                  flag = false;              if( flag )                  Array.Clear( _buffer, _length, num - _length );              _length = num;          }          if( count <= 8 && buffer != _buffer )          {              int num2 = count;              while( --num2 >= 0 )                  _buffer[_position + num2] = buffer[offset + num2];          }          else              Array.Copy( buffer, offset, _buffer, _position, count );          _position = num;      }     public override void WriteByte( byte value )      {          if( _position >= _length )          {              int num   = _position + 1;              bool flag = _position > _length;              if( num >= _capacity && EnsureCapacity( num ) )                  flag = false;              if( flag )                  Array.Clear( _buffer, _length, _position - _length );              _length = num;          }          _buffer[_position++] = value;      }     public override bool CanRead      {          get { return true; }      }     public override bool CanSeek      {          get { return true; }      }     public override bool CanWrite      {          get { return true; }      }     public virtual int Capacity      {          get { return _capacity - _origin; }          set          {              if( value != _capacity )              {                  if( value > 0 )                  {                      byte[] dst = new byte[value];                      if( _length > 0 )                          Array.Copy( _buffer, 0, dst, 0, _length );                      _buffer = dst;                  }                  else                      _buffer = null;                  _capacity = value;              }          }      }     public override long Length      {          get { return (long)(_length - _origin); }      }     public override long Position      {          get { return (long)(_position - _origin); }          set { _position = _origin + ((int)value); }      }     internal void Write7BitEncodedInt( int value )      {          BinWriter.Write7BitEncodedInt( this, value );      }     internal int Read7BitEncodedInt()      {          return BinReader.Read7BitEncodedInt( this );      }  }
  任何想用这个工具进行序列化的类,只需要实现下面的借口:
public abstract BinSerializer
{  // 将各成员的数据写入 stream,这是一个辅助函数,派生类一般不需要重载  public virtual void ToStream( MemStream stream )  {      ToBinary( new BinWriter( stream, Encoding.UTF8 ) );  } // 从 stream 中读取各成员的数据,这是一个辅助函数,派生类一般不需要重载  public virtual void FromStream( MemStream stream )  {      FromBinary( new BinReader( stream, Encoding.UTF8 ) );  } protected abstract void ToBinary( BinWriter bw ); // 派生类实现此函数  protected abstract void FromBinary( BinReader br ); // 派生类实现此函数

}

举个例子:

public class MyTest : BinSerializer

{  public int  x;  public bool y;  public string z;  public DateTime dt;
      protected overide void ToBinary( BinWriter bw ) // 派生类实现此函数 {      bw.Write( x );      bw.Write( y );      bw.Write( z );      bw.Write( dt );  }

    protected overide void FromBinary( BinReader br ) // 派生类实现此函数  {      x = br.ReadInt32();      y = br.ReadBool();      z = br.ReadString();      dt = br.ReadDate();  }
}
using( MemStream stream = new MemStream() )

{ MyTest tester = new MyTest(); tester.ToStream( stream ); // 将各成员的数据写入 stream stream.Position = 0;  // 将流的位置 Seek 到 Beginning,下面将从它读取数据 MyTest newTester = new MyTest();  newTester.FromStream( stream ); // 从 stream 中读取各成员的数据
}
  虽然没有 C# 的 [Serializable] 类属性来得简单,但这对各平台互操作有利。是 [Serializable] 和 Json 之间的一种折中方案。
同时,它不会引入一堆 Json 和 XML 中的描述标签,而是直接反映内存数据。确保各个成员的读写顺序一致,并确保各个平台的 int, long, uint, ulong 的字节数一致,就可以交互

关于 "在 MVC Controller 中,不依靠定制路由,随心所欲地拦截请求和处理请求,想如何输入输出都" 就介绍到此。希望多多支持编程宝库

本文讲解"HashSet源码分析:JDK源码系列",用于解决相关问题。1.简介继续分析源码,上一篇文章把HashMap的分析完毕。本文开始分析HashSet简单的介绍一下。HashSet是一个无重复元 ...