zoukankan      html  css  js  c++  java
  • BeetleX之webapi自定义响应内容

            输出内容多样性在webapi服务中比较普遍的,有的情况使用json,xml,图片和二进制流下载等等;为了适应用不同情况的需要,组件支持自定义内容输出。接下来的主要描述组件在webapi如何定义各种内容输出来满足实际应用的需要。

    规则

            组件通过接口来规范自定义内容输出:

        public interface IResult
        {
            //指定输出的ContentType
            IHeaderItem ContentType { get; }
            //http body长度,此值可以是零,当为零的时候组件会自动计算
            int Length { get; set; }
            //输出前用于设置相关http头信息
            void Setting(HttpResponse response);
            //是否存在http body内容
            bool HasBody { get; }
            //写入Http body
            void Write(PipeStream stream, HttpResponse response);
        }
    以上是定义内容输出的接口规则,所有自定义输出都必须实现这规则。以下是针对text/plain; charset=UTF-8的基础类输出抽象类
        public abstract class ResultBase : IResult
        {
            public virtual IHeaderItem ContentType => ContentTypes.TEXT_UTF8;
            public virtual int Length { get; set; }
            public virtual bool HasBody => true;
            public virtual void Setting(HttpResponse response)
            {}
            public virtual void Write(PipeStream stream, HttpResponse response)
            {}
        }
    在以上抽象类的基础上,组件扩展了很多基础的输出类

    服务内部错误

    public class InnerErrorResult : ResultBase
    {
        public InnerErrorResult(string code, string messge)
        {
            Code = code;
            Message = messge;
        }
        public InnerErrorResult(string message, Exception e, bool outputStackTrace) : this("500", message, e, outputStackTrace)
        {
        }
        public InnerErrorResult(string code, string message, Exception e, bool outputStackTrace)
        {
            Code = code;
            Message = message;
            Error = e.Message;
            if (outputStackTrace)
                SourceCode = e.StackTrace;
            else
                SourceCode = "";
        }
        public string Message { get; set; }
        public string Error { get; set; }
        public string Code { get; set; }
        public string SourceCode { get; set; }
        public override bool HasBody => true;
        public override void Setting(HttpResponse response)
        {
            response.Code = Code;
            response.CodeMsg = Message;
            response.Request.ClearStream();
        }
        public override void Write(PipeStream stream, HttpResponse response)
        {
            if (!string.IsNullOrEmpty(Error))
            {
                stream.WriteLine(Error);
            }
            if (!string.IsNullOrEmpty(SourceCode))
            {
                stream.WriteLine(SourceCode);
            }
        }
    }

    以上是组件内部错误定义的输出类,所有处理的内部异常响应都是使用这类进行输出。

    if (!mIPLimit.ValidateRPS(request))
    {
        token.KeepAlive = false;
        InnerErrorResult innerErrorResult = 
        new InnerErrorResult("400", $"{request.RemoteIPAddress} request limit!");
        response.Result(innerErrorResult);
        return;
    }

    以上是组件内部针对IP做的一个请求限制错误输出。

    JSON输出

    public class JsonResult : ResultBase
    {
        public JsonResult(object data, bool autoGzip = false)
        {
            Data = data;
            mAutoGzip = autoGzip;
        }
        public object Data { get; set; }
        private bool mAutoGzip = false;
        private ArraySegment<byte> mJsonData;
        [ThreadStatic]
        private static System.Text.StringBuilder mJsonText;
        private void OnSerialize(HttpResponse response)
        {
            if (mJsonText == null)
                mJsonText = new System.Text.StringBuilder();
            mJsonText.Clear();
            JsonSerializer serializer = response.JsonSerializer;
            System.IO.StringWriter writer = new System.IO.StringWriter(mJsonText);
            JsonTextWriter jsonTextWriter = new JsonTextWriter(writer);
            serializer.Serialize(jsonTextWriter, Data);
            var charbuffer = System.Buffers.ArrayPool<Char>.Shared.Rent(mJsonText.Length);
            mJsonText.CopyTo(0, charbuffer, 0, mJsonText.Length);
            try
            {
                var bytes = System.Buffers.ArrayPool<byte>.Shared.Rent(mJsonText.Length * 6);
                var len = System.Text.Encoding.UTF8.GetBytes(charbuffer, 0, mJsonText.Length, bytes, 0);
                mJsonData = new ArraySegment<byte>(bytes, 0, len);
            }
            finally
            {
                System.Buffers.ArrayPool<char>.Shared.Return(charbuffer);
            }
        }
        public override void Setting(HttpResponse response)
        {
            base.Setting(response);
            if (this.mAutoGzip)
                OnSerialize(response);
            if (mAutoGzip && mJsonData.Count > 1024 * 2)
            {
                response.Header.Add("Content-Encoding", "gzip");
            }
        }
        public override IHeaderItem ContentType => ContentTypes.JSON;
        public override bool HasBody => true;
        public override void Write(PipeStream stream, HttpResponse response)
        {
            if (mAutoGzip)
            {
                try
                {
                    if (mJsonData.Count > 1024 * 2)
                    {
                        using (stream.LockFree())
                        {
                            using (var gzipStream = new GZipStream(stream, CompressionMode.Compress, true))
                            {
                                gzipStream.Write(mJsonData.Array, mJsonData.Offset, mJsonData.Count);
                                gzipStream.Flush();
                            }
                        }
                    }
                    else
                    {
                        stream.Write(mJsonData.Array, mJsonData.Offset, mJsonData.Count);
                    }
                }
                finally
                {
                    System.Buffers.ArrayPool<byte>.Shared.Return(mJsonData.Array);
                }
            }
            else
            {
                using (stream.LockFree())
                {
                    response.JsonSerializer.Serialize(response.JsonWriter, Data);
                    response.JsonWriter.Flush();
                }
            }
        }
    }

    以上是组件内部实现的Json输出,不过这个JsonResult实现有些复杂,主要是可以根据内容大小来自动进行Gzip输出。

    Websocket升级响应

    public class UpgradeWebsocketResult : ResultBase
    {
        public UpgradeWebsocketResult(string websocketKey)
        {
            WebsocketKey = websocketKey;
        }
        public string WebsocketKey { get; set; }
        public override bool HasBody => false;
        public override void Setting(HttpResponse response)
        {
            response.Code = "101";
            response.CodeMsg = "Switching Protocols";
            response.Header.Add(HeaderTypeFactory.CONNECTION, "Upgrade");
            response.Header.Add(HeaderTypeFactory.UPGRADE, "websocket");
            response.Header.Add(HeaderTypeFactory.SEC_WEBSOCKET_VERSION, "13");
            SHA1 sha1 = new SHA1CryptoServiceProvider();
            byte[] bytes_sha1_in = Encoding.UTF8.GetBytes(WebsocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
            byte[] bytes_sha1_out = sha1.ComputeHash(bytes_sha1_in);
            string str_sha1_out = Convert.ToBase64String(bytes_sha1_out);
            response.Header.Add(HeaderTypeFactory.SEC_WEBSOCKT_ACCEPT, str_sha1_out);
    } }

    以上是针对websocket握手升级响应的内容

    文件下载

            在web服务中很多时候需要下载指定的文件或二进制内容,以下是转门针对这些需求场制定的响应对象.

    public class DownLoadResult : BeetleX.FastHttpApi.IResult
    {
        public DownLoadResult(string text, string fileName, IHeaderItem contentType)
        {
            mData = Encoding.UTF8.GetBytes(text);
            mFileName = System.Web.HttpUtility.UrlEncode(fileName);
            if (contentType != null)
                mContentType = contentType;
        }
    
        public DownLoadResult(byte[] data, string fileName, IHeaderItem contentType)
        {
            mData = data;
            mFileName = System.Web.HttpUtility.UrlEncode(fileName);
            if (contentType != null)
                mContentType = contentType;
        }
    
        private string mFileName;
    
        private byte[] mData;
    
        private IHeaderItem mContentType = ContentTypes.OCTET_STREAM;
    
        public IHeaderItem ContentType => mContentType;
    
        public int Length { get; set; }
    
        public bool HasBody => true;
    
        public void Setting(HttpResponse response)
        {
            response.Header.Add("Content-Disposition", $"attachment;filename={mFileName}");
        }
        public virtual void Write(PipeStream stream, HttpResponse response)
        {
            stream.Write(mData);
        }
    }

    应用示例

        [Controller]
        class Webapi
        {
            public object Default()
            {
                return new { Name = "BeetleX", Email = "Admin@beetlex.io" };
            }
            public object Json()
            {
                return new JsonResult(new { Name = "BeetleX", Email = "Admin@beetlex.io" });
            }
            public object Download()
            {
                var txt = JsonConvert.SerializeObject(new { Name = "BeetleX", Email = "Admin@beetlex.io" });
                return new DownLoadResult(txt, "json.txt", ContentTypes.JSON);
            }
            public object Image()
            {
                var str = "...";
                var data = Convert.FromBase64String(str);
                return new ImageResult(new ArraySegment<byte>(data, 0, data.Length), "image/jpeg");
            }
        }

      

     下载示例 

    链接:https://pan.baidu.com/s/1GX2D-Qwo9ep1gHU-sPZkUA 

    提取码:py6m 

  • 相关阅读:
    Chromium 和Webkit 3月14日的最近进展:多标签选择、标签标题省略、动画API和新的Chromium Logo
    奇妙的HTML5 Canvas动画实例
    XNA那些事(一) 框架工作原理
    实现跨浏览器的HTML5占位符
    编写超级可读代码的15个最佳实践
    谈HTML5和CSS3的国际化支持
    HTML5 Guitar Tab Player
    如何设置让网站禁止被爬虫收录?robots.txt
    YourPHP笔记
    Robots.txt  禁止爬虫
  • 原文地址:https://www.cnblogs.com/smark/p/13806610.html
Copyright © 2011-2022 走看看