输出内容多样性在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

BeetleX之webapi自定义响应内容的更多相关文章

  1. .net core webapi通过中间件获取请求和响应内容

    本文主要根据中间件来实现对.net core webapi中产生的请求和响应数据进行获取并存入日志文件中: 这里不详细介绍日志文件的使用.你可以自己接入NLog,log4net,Exceptionle ...

  2. 第九篇 :微信公众平台开发实战Java版之如何实现自定义分享内容

    第一部分:微信JS-SDK介绍 微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包. 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照.选图.语音.位置等手机系统 ...

  3. C#如何拦截 Webbrowser Control的响应内容

    场景目标 假如Webbrowser中的一个页面打开后第一件事就是执行了alert,我们想要阻止它该如何做? <html> <head> <script src=" ...

  4. .net core MVC 通过 Filters 过滤器拦截请求及响应内容

    前提: 需要nuget   Microsoft.Extensions.Logging.Log4Net.AspNetCore   2.2.6: Swashbuckle.AspNetCore 我暂时用的是 ...

  5. 自定义响应结构 AjaxResult()

    package com.dsj.gdbd.utils.ajax; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxm ...

  6. ASP.NET Core 6框架揭秘实例演示[34]:缓存整个响应内容

    我们利用ASP.NET开发的大部分API都是为了对外提供资源,对于不易变化的资源内容,针对某个维度对其实施缓存可以很好地提供应用的性能.<内存缓存与分布式缓存的使用>介绍的两种缓存框架(本 ...

  7. .NET WebAPI 自定义 NullableConverter 解决请求入参 “”空字符触发转换异常问题

    最近在项目中启用了Nullable 可为空的类型,这个特性确实很好用,在 WebAPI 的入参上可以直接采用 ? 来标记一个字段是否允许为空,但是使用过程中遇到了如下一个问题,比如创建部门接口 我们定 ...

  8. 自定义响应结构 Json格式转换 工具类

    import java.util.List; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterx ...

  9. Node.js与Sails~自定义响应体responses

    回到目录 在Node.js里,你可以控制请求和响应,自己可以定义自己的响应方式,如对文本如何响应,对json如何响应,对图像流如何响应等等,而这些在Sails架构里,变得更加容易和清晰了,它位于项目的 ...

随机推荐

  1. WinForm使用Setuo Project打包安装包 (附带vs2019 InstallerProjects安装程序)

    vs2019 InstallerProjects安装程序地址: 链接:https://pan.baidu.com/s/1K5iDuQT4CBBw2dJjRLqhjg提取码:dfhy 转载至https: ...

  2. vue相关知识点及面试

    ### vue #### vue生命周期 beforeCreated `实例初始化,数据观察和event/watch事件配置之前被调用` created `实例创建后立即调用,数据观测,数据和方法运算 ...

  3. Java 得到指定时间加半个小时之后得时间

    Calendar c = Calendar.getInstance(); c.setTime(cur); //设置时间 c.add(Calendar.MINUTE, 1); //日期分钟加1,Cale ...

  4. leetcode刷题-74搜索二维矩阵

    题目 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值.该矩阵具有如下特性: 每行中的整数从左到右按升序排列.每行的第一个整数大于前一行的最后一个整数.示例 1: 输入:matrix ...

  5. vueJs 安装

    1.下载nodeJs 可前往 https://www.cnblogs.com/takeyblogs/p/13600124.html 这里下载 2.由于 npm 安装速度慢,本教程使用了淘宝的镜像及其命 ...

  6. 面试官问我:看过sharding-jdbc的源码吗?我吧啦吧啦说了一通!!

    写在前面 在产品初期快速迭代的过程中,往往为了快速上线而占据市场,在后端开发的过程中往往不会过多的考虑分布式和微服务,往往会将后端服务做成一个单体应用,而数据库也是一样,最初会把所有的业务数据都放到一 ...

  7. ctfhub sql注入 整数型注入

    整数型注入 手工注入 1.查看是否存在sql注入,及sql注入类型 2.确定列数 3.确定注入点,以及数据库版本,数据库名称 4.查表名 5.查字段名以及flag值 获得flag值 sqlmap做法 ...

  8. 第1课 - 学习 Lua 的意义

    第1课 - 学习 Lua 的意义 1.Lua 简介 (1) 1993年.巴西 (2) 小巧精致的脚本语言,大小只有 200K (3) 用标准C语言写成,能够在所有的平台上编译运行 (4) 发明的目标是 ...

  9. 分布式系统监视zabbix讲解八之自动发现/自动注册

    自动发现(LLD) 概述 自动发现(LLD)提供了一种在计算机上为不同实体自动创建监控项,触发器和图形的方法.例如,Zabbix可以在你的机器上自动开始监控文件系统或网络接口,而无需为每个文件系统或网 ...

  10. Mysql-Innodb : 从一个字节到整个数据库表了解物理存储结构和逻辑存储结构

    首先要从Innodb怎么看待磁盘物理空间说起   一块原生的(Raw)物理磁盘,可以把他看成一个字节一个字节单元组成的物理存储介质   如果要在这块原生物理空间中插入一条记录,不能单单只插入数据,还需 ...