MVC in Asp.net is mainly oriented to Content-Type of "text" type to handle HTTP requests. In addition to file transfer, even json and xml are text types. Therefore, MVC naturally handles input and output of text type very well. Sometimes, however, this does not satisfy us. What happens when we want to transfer binary byte [] arrays, serialize structured data, and handle any special requests? Do you have to code them in base64 or customize a routing for a particular request, such as going to IHttpHandler/IRouteHandler? I am also a beginner of Asp.net and a beginner of MVC. At first, I followed this train of thought, and found that the more routing is added, the more unexpected troubles will be brought to the program. I added a sentence: routes. Add ("myRouteName", "new Route ("ac/aucenter"), new MyHttpRouteHandler ()); all links that cause the view page become http://host/ac/aucenter?, and the controller designated for these links, Action, all become parameters of the Url. Of course, I can not add this routing, but encrypt the "encrypted binary data" into base64 and then transmit it. But how can I do base64 encoding well in a C++ client DLL? In C++, it is very difficult to interact with asp.net server. It is the simplest to send and receive binary and serialized data directly. Anyway, I must not add routing. Any way I think about on the road is a patching way of thinking. Based on my more than ten years of experience with C++ and Windows, I guess that MVC will enable me to intercept HTTP requests arbitrarily, not let it enter the system's routing for a loop, but let me capture them before entering the routing, so as to transfer them to my own processing. But after searching for half a day on the internet, I haven't found the right one. Some people say that they add Filter, others say that they deal with 404 errors. There are many ways to deal with them, but they all have problems of one kind or another. For example, add Filter, since I do not call routes.Add(), then add Filter is useless; deal with 404? This requires global interception. Intercept 404 in Application. Are you sure? There are also calls to server.GetLastError(), which is obviously the last error to fetch the server. So many errors occur at the same time, you intercept the last error? On the way, I couldn't find a way out, so I turned to Controller itself to see if it could process binary data directly. I paid special attention to how Controller sent the response data back to the client. ContentResult, Json Result, FileStream Result and so on are all used to send back the response. So I looked over. ContentResult's. NET source code, and then suddenly realized.
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; } }
In ContentResult, its Execute Result () calls HttpContext.Response.Write() to write the data to the response and send it back to the client. It's just that ExecuteResult() is not called directly by us, but by MVC after the ContentResult object is returned after the Action() processing of our Controller. Why do I have to ask MVC to call Response.Write()? Can't I call it directly? After I call it, I return an Empty Result and let it do nothing. Is that all right? Looking at Empty Result's. NET source code again, we find that it does nothing and is empty. It implements only one case model internally. Looking at the Request request, I found that the server can actually receive the binary stream, as follows:
public class TestController : Controller { [HttpPost] public ActionResult Index() { MemoryStream inStream = new MemStream( Math.Max( Request.TotalBytes, 64 ) ); Request.InputStream.CopyTo( inStream ); // Processing inStream, putting the response data in rspData, and then: Response.OutputStream.Write( rspData, 0, rspData.Length ); return EmptyResult.Instance; } }
It's as simple as that. It's up to you to handle the inflow and outflow of HTTP. You can also derive your own class from ActionResult, such as MyResult, which sends back the response data rspData in its ExecuteResult() interface implementation, referring to the code of ContentResult. And the TestController.Index() above returns your new MyResult (rspData).
Next, let's look at the interoperability of various programming languages. Everyone agrees to serialize data with Json and Xml, and then interact with each other on different platforms, languages and operating systems. In my opinion, this is not necessarily easy to use. Let's take a look at a serialization tool I've provided. It's easy to use, takes up less memory, and can be cross-platform with minor modifications. This tool, which provides three classes and an interface, is written by copying the source code of C# Emory Stream, BinaryWriter and BinaryReader. There are many minor problems in the implementation of C#, for example, it can not handle null strings, and this tool can serialize null strings. In addition, these classes of C # make a bunch of useless Dispose(), and sometimes inexplicably, their internal Stream is Dispose(). This tool, however, ignores this, and implies only Dispose() inheriting the base class. Whoever creates Stream is responsible for Dispose, otherwise it will be messy.
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> /// Read the sequence of bytes from the current stream and raise the number of bytes read from the position in the stream. /// </summary> /// The zero-based byte offset in <param name="offset">buffer, where data read from the current stream is stored. </param> /// <param name="count">The maximum number of bytes to read from the current stream </param> /// The total number of bytes read into the buffer. If the number of bytes currently available is not as large as the number of bytes requested, the total number of bytes may be less than the number of bytes requested; if the end of the stream has been reached, it is zero (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 ); } }
Any class that wants to be serialized with this tool needs only the following excuses:
public abstract BinSerializer { // Write the data of each member to the stream, which is an auxiliary function. Derived classes generally do not need to be overloaded. public virtual void ToStream( MemStream stream ) { ToBinary( new BinWriter( stream, Encoding.UTF8 ) ); } // Read the data of each member from stream. This is an auxiliary function. Derived classes generally do not need to be overloaded. public virtual void FromStream( MemStream stream ) { FromBinary( new BinReader( stream, Encoding.UTF8 ) ); } protected abstract void ToBinary( BinWriter bw ); // Derived classes implement this function protected abstract void FromBinary( BinReader br ); // Derived classes implement this function }
For instance:
public class MyTest : BinSerializer { public int x; public bool y; public string z; public DateTime dt; protected overide void ToBinary( BinWriter bw ) // Derived classes implement this function { bw.Write( x ); bw.Write( y ); bw.Write( z ); bw.Write( dt ); } protected overide void FromBinary( BinReader br ) // Derived classes implement this function { x = br.ReadInt32(); y = br.ReadBool(); z = br.ReadString(); dt = br.ReadDate(); } }
using( MemStream stream = new MemStream() ) { MyTest tester = new MyTest(); tester.ToStream( stream ); // Write members'data to stream stream.Position = 0; // Seek the location of the stream to Beginning, from which data will be read below MyTest newTester = new MyTest(); newTester.FromStream( stream ); // Read the data of each member from stream }
Although it's easy to do without the C # [Serializable] class attributes, it's good for platform interoperability. It's a compromise between [Serializable] and Json. At the same time, it does not introduce a bunch of description tags in Json and XML, but directly reflects memory data. Ensure that members read and write in the same order, and that the number of bytes of int, long, uint, ulong on each platform is the same, so that they can interact.