您好,登錄后才能下訂單哦!
Asp.net 中的 MVC,其主要面向 “text” 類型的 Content-Type 來處理 HTTP 請(qǐng)求,除了文件傳輸之外,就連 json、xml 也都是文本類型。
因此,對(duì)于 text 類型的輸入輸出,MVC 自然處理得很好??捎袝r(shí)候,這并不能令我們滿意。
當(dāng)我們要傳輸二進(jìn)制 byte[] 數(shù)組、序列化結(jié)構(gòu)數(shù)據(jù)、以及任何特殊請(qǐng)求的處理時(shí),該怎么辦呢?難道非要將它們以 base64 編碼,抑或是為特殊請(qǐng)求定制一個(gè)路由,譬如轉(zhuǎn)到 IHttpHandler/IRouteHandler 去處理嗎?
我也是一個(gè) Asp.net 的初學(xué)者,更是一個(gè) MVC 的初學(xué)者,剛開始我也跟著這個(gè)思路去做,結(jié)果發(fā)現(xiàn)越增加路由,就會(huì)給程序帶來意想不到的麻煩。我就加了一句話: routes.Add( "myRouteName", new Route( "ac/aucenter", new MyHttpRouteHandler() ) ); 就引起視圖頁(yè)面的所有鏈接,全都變成了 http://host/ac/aucenter?,而為這些鏈接指定的控制器,Action,全都變成了該 Url 的參數(shù)。
當(dāng)然,我可以不增加這個(gè)路由,而是將 “加密后的二進(jìn)制數(shù)據(jù)” 進(jìn)行 base64 編碼后再傳輸,可是,這在一個(gè) C++ 客戶端 DLL 中,我又怎么能做好 base64 編碼呢?在 C++ 中,與 asp.net 服務(wù)端進(jìn)行交互非常困難,直接發(fā)送和接收二進(jìn)制、序列化數(shù)據(jù)最簡(jiǎn)單。反正,我一定不要增加路由,在路由上想任何辦法,都是 “打補(bǔ)丁” 的思維方式。
本著我對(duì) C++ 和 Windows 十多年的經(jīng)驗(yàn),我猜想?MVC 一定能讓我任意地?cái)r截 HTTP 請(qǐng)求,不讓它進(jìn)入系統(tǒng)的路由中逗一圈,而是在進(jìn)入路由之前,讓我捕獲,以轉(zhuǎn)入我自己的處理。但是在網(wǎng)上找了半天也沒找到如意的,有說加 Filter 的,有說處理 404 錯(cuò)誤的,方法非常多,但都有這樣那樣的問題。 譬如說,加 Filter 吧,我既然都不調(diào)用?routes.Add() ,那么加 Filter 沒用??;處理 404? 這得要全局?jǐn)r截,在 Application 中攔截 404,你放心嗎?還居然有 server.GetLastError() 的調(diào)用都來了,這明顯是取服務(wù)器最后一個(gè)錯(cuò)誤啊,那么多錯(cuò)誤同時(shí)發(fā)生,你就攔截最后一個(gè)錯(cuò)誤?
在路由上,我實(shí)在找不到辦法,于是就轉(zhuǎn)入 Controller 本身,看看它能否直接處理二進(jìn)制數(shù)據(jù),特別關(guān)注 Controller 是怎么把響應(yīng)數(shù)據(jù)發(fā)回給客戶端的,ContentResult,JsonResult,F(xiàn)ileStreamResult?等等,都是用來發(fā)回響應(yīng)的,我于是翻看了下?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() 不就是調(diào)用 HttpContext.Response.Write() 把數(shù)據(jù)寫入 response,發(fā)回客戶端的嗎。只是,ExecuteResult() 不是由我們直接調(diào)用的,而是在我們的?Controller 的 Action() 處理結(jié)束返回該?ContentResult 對(duì)象后,由 MVC 接著調(diào)用的。
為啥非要讓 MVC 調(diào)用?Response.Write(),我直接調(diào)用不也可以嗎?我調(diào)用完之后,返回一個(gè) EmptyResult,讓它啥也不干,不就好了嗎?
再翻看?EmptyResult 的 .NET 源碼發(fā)現(xiàn),它確實(shí)啥也不干,空空如也。其內(nèi)部只實(shí)現(xiàn)了一個(gè)單例模型。
再來看看 Request 請(qǐng)求,我測(cè)試后發(fā)現(xiàn),服務(wù)器端確實(shí)能接收到二進(jìn)制流,如下所示:
public class TestController : Controller
{
[HttpPost]
public ActionResult Index()
{
MemoryStream inStream = new MemStream( Math.Max( Request.TotalBytes, 64 ) );
Request.InputStream.CopyTo( inStream );
// 對(duì) inStream 做處理,將響應(yīng)數(shù)據(jù)放在 rspData 中,然后:
Response.OutputStream.Write( rspData, 0, rspData.Length );
return EmptyResult.Instance;
}
}
就這么簡(jiǎn)單,你想怎樣處理 HTTP 的流進(jìn)流出,都隨你的意。
你也可以從?ActionResult 派生一個(gè)自己的類,例如 MyResult,在其?ExecuteResult() 接口實(shí)現(xiàn)中,參照?ContentResult 的代碼,把響應(yīng)數(shù)據(jù)?rspData 發(fā)回。而在上面的?TestController.Index() 返回你的 new?MyResult(?rspData ).
?
接下來,我們來看看,各種編程語言的互操作問題,大家都贊同以 Json,Xml 來序列化數(shù)據(jù),然后在各個(gè)平臺(tái),各種語言,各個(gè)操作系統(tǒng)之間交互,以我看,這并不一定簡(jiǎn)單易用。來看看我提供的一個(gè)序列化工具,簡(jiǎn)單易用,內(nèi)存占用少,稍作修改就可以跨平臺(tái)。
這個(gè)工具,提供 3 個(gè)類和一個(gè)接口,就是抄的 C# 的 MemoryStream,BinaryWriter,BinaryReader 的源碼來編寫。C# 的實(shí)現(xiàn)上,有諸多的小問題,例如,它無法處理 null 字符串,而這個(gè)工具能序列化 null 字符串。此外,C# 的這些類,搞了一堆沒啥用的 Dispose(),有時(shí)候莫名其妙地,其內(nèi)部的 Stream 就被 Dispose() 了。而這個(gè)工具,不用理會(huì)這些,它只隱含繼承基類的 Dispose()。誰創(chuàng)建的 Stream,就由誰負(fù)責(zé)?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>
/// 從當(dāng)前流讀取字節(jié)序列,并將此流中的位置提升讀取的字節(jié)數(shù)。
/// </summary>
/// <param name="offset">buffer 中的從零開始的字節(jié)偏移量,從此處開始存儲(chǔ)從當(dāng)前流中讀取的數(shù)據(jù)。</param>
/// <param name="count">要從當(dāng)前流中最多讀取的字節(jié)數(shù)</param>
/// <returns>讀入緩沖區(qū)中的總字節(jié)數(shù)。如果當(dāng)前可用的字節(jié)數(shù)沒有請(qǐng)求的字節(jié)數(shù)那么多,則總字節(jié)數(shù)可能小于請(qǐng)求的字節(jié)數(shù);如果已到達(dá)流的末尾,則為零 (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 );
}
}
任何想用這個(gè)工具進(jìn)行序列化的類,只需要實(shí)現(xiàn)下面的借口:
public abstract BinSerializer
{
// 將各成員的數(shù)據(jù)寫入 stream,這是一個(gè)輔助函數(shù),派生類一般不需要重載
public virtual void ToStream( MemStream stream )
{
ToBinary( new BinWriter( stream, Encoding.UTF8 ) );
}
// 從 stream 中讀取各成員的數(shù)據(jù),這是一個(gè)輔助函數(shù),派生類一般不需要重載
public virtual void FromStream( MemStream stream )
{
FromBinary( new BinReader( stream, Encoding.UTF8 ) );
}
protected abstract void ToBinary( BinWriter bw ); // 派生類實(shí)現(xiàn)此函數(shù)
protected abstract void FromBinary( BinReader br ); // 派生類實(shí)現(xiàn)此函數(shù)
}
舉個(gè)例子:
public class MyTest :?BinSerializer
{
public int x;
public bool y;
public string z;
public DateTime dt;
? ?
protected overide void ToBinary( BinWriter bw )?// 派生類實(shí)現(xiàn)此函數(shù)
{
bw.Write( x );
bw.Write( y );
bw.Write( z );
bw.Write( dt );
}
? ? protected overide void FromBinary( BinReader br ) // 派生類實(shí)現(xiàn)此函數(shù)
{
x = br.ReadInt32();
y = br.ReadBool();
z = br.ReadString();
dt = br.ReadDate();
}
}
using( MemStream stream = new MemStream() )
{
MyTest tester = new?MyTest();
tester.ToStream( stream ); // 將各成員的數(shù)據(jù)寫入 stream
stream.Position = 0;? // 將流的位置 Seek 到 Beginning,下面將從它讀取數(shù)據(jù)
MyTest newTester = new?MyTest();?
newTester.FromStream(?stream );?// 從 stream 中讀取各成員的數(shù)據(jù)
}
雖然沒有 C# 的?[Serializable] 類屬性來得簡(jiǎn)單,但這對(duì)各平臺(tái)互操作有利。是?[Serializable] 和 Json 之間的一種折中方案。
同時(shí),它不會(huì)引入一堆 Json 和 XML 中的描述標(biāo)簽,而是直接反映內(nèi)存數(shù)據(jù)。確保各個(gè)成員的讀寫順序一致,并確保各個(gè)平臺(tái)的 int, long, uint, ulong 的字節(jié)數(shù)一致,就可以交互
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。