using System; using System.IO; namespace Apewer.Web { /// Response 模型。 public abstract class ApiModel { #region private int _expires = 0; internal ApiRequest _request; internal ApiResponse _response; internal ApiOptions _options; internal ApiProvider _provider; static int SafeExpires(int seconds) { var s = seconds; if (s < 0) s = 0; if (s > 2592000) s = 2592000; return s; } #endregion #region 内部属性和方法。 /// 处理当前模型的 API 请求。 protected ApiRequest Request { get => _request; } /// 处理当前模型的 API 响应。 protected ApiResponse Response { get => Response; } /// 处理当前模型的 API 选项。 protected ApiOptions Options { get => _options; } /// 处理当前模型的服务程序实例。 protected ApiProvider Provider { get => _provider; } /// 在 Response 头中添加用于设置文件名的属性。 protected void SetAttachment() { if (Provider == null) return; var name = Attachment; if (string.IsNullOrEmpty(name)) return; var encoded = TextUtility.EncodeUrl(name); Provider.SetHeader("Content-Disposition", $"attachment; filename={encoded}"); } /// 以指定参数输出。 protected void Output(byte[] bytes) { if (Provider == null) return; try { SetAttachment(); Provider.SetCache(Expires); Provider.SetContentType(ContentType); var length = bytes == null ? 0 : bytes.Length; Provider.SetContentLength(length); if (length > 0) Provider.ResponseBody().Write(bytes); } catch { } } /// 以指定参数输出。 protected void Output(Stream stream, bool dispose) { if (Provider == null) return; try { SetAttachment(); Provider.SetCache(Expires); Provider.SetContentType(ContentType); Provider.SetContentLength(stream.Length - stream.Position); Provider.ResponseBody().Write(stream); } catch { } if (dispose) RuntimeUtility.Dispose(stream); } #endregion /// 内容类型。 public virtual string ContentType { get; set; } /// 响应缓存的过期时间,以秒为单位。 public virtual int Expires { get => _expires; set => _expires = SafeExpires(value); } /// 设置文件名,告知客户端此附件处理此响应。 public virtual string Attachment { get; set; } /// 执行输出。 /// 此方法由 API 调用器发起调用,用户程序不应主动调用。 /// public abstract void Output(); /// 创建对象实例,并设置默认属性。 public ApiModel() { ContentType = "application/octet-stream"; Expires = 0; Attachment = null; } } /// 输出二进制的 Response 模型。 public class ApiBytesModel : ApiModel { /// 向 Body 写入的字节数组。 public byte[] Bytes { get; set; } /// 输出字节数组。 public override void Output() => Output(Bytes); } /// 输出二进制的 Response 模型。 public class ApiStreamModel : ApiModel, IDisposable { /// 将要读取的流,用于向 Body 写入。 public Stream Stream { get; set; } /// 执行输出后释放流。 /// 默认值:TRUE。 public bool AutoDispose { get; set; } /// 输出流。 public override void Output() => Output(Stream, AutoDispose); /// 当指定 AutoDispose 属性时释放流。 public void Dispose() { if (AutoDispose) RuntimeUtility.Dispose(Stream); } /// 创建对象实例,并设置默认属性。 public ApiStreamModel() => AutoDispose = true; } /// 输出二进制的 Response 模型。 public class ApiFileModel : ApiModel { /// 将要读取的文件所在路径,用于向 Body 写入。 public string Path { get; set; } /// 输出指定路径的文件。 public override void Output() { try { if (!File.Exists(Path)) return; var info = new FileInfo(Path); if (string.IsNullOrEmpty(Attachment)) Attachment = info.Name; var stream = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read); Output(stream, true); } catch { } } } /// 输出文本的 Response 模型。 public class ApiTextModel : ApiModel { /// 自定义文本。 public string Text { get; set; } /// 输出文本。 public override void Output() => Output(TextUtility.Bytes(Text)); /// 创建对象实例,并设置默认属性。 public ApiTextModel() => ContentType = "text/plain; charset=utf-8"; } /// 输出文本的 Response 模型。 public class ApiJsonModel : ApiModel { /// Json 对象。 public Json Json { get; set; } /// 缩进排版。 /// 默认值:TRUE public bool Indented { get; set; } /// 转为属性名为驼峰形式。 /// 默认值:FALSE public bool Camel { get; set; } /// 输出文本。 public override void Output() { var json = (Json != null && Json.Available) ? Json : Json.NewObject(); if (Camel) Json.Camel(json); Output(TextUtility.Bytes(json.ToString(Indented))); } /// 创建对象实例,并设置默认属性。 public ApiJsonModel() { ContentType = "text/plain; charset=utf-8"; Indented = true; } } /// 输出重定向的 Response 模型。 public class ApiRedirectModel : ApiModel { /// 将要重定向的位置。 public string Location { get; set; } /// 执行重定向。 public override void Output() { var location = Location; if (string.IsNullOrEmpty(location)) return; if (Provider == null) return; Provider.SetRedirect(Location); } } /// 输出带有指定 Status 的 Response 模型。 public class ApiStatusModel : ApiModel { /// 状态。 /// 默认值:200。 public int Status { get; set; } = 200; /// 向 Body 写入的字节数组。 public byte[] Bytes { get; set; } /// 执行重定向。 public override void Output() { var status = Status > 0 ? Status : 200; Provider.SetStatus(status.ToString()); Output(Bytes); } /// public ApiStatusModel() { } /// public ApiStatusModel(int status) { Status = status; } } }