You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
7.7 KiB

#if NETCORE
using Apewer.Network;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Apewer.Web
{
/// <summary>用于网站的服务程序。</summary>
public class AspNetCoreProvider : ApiProvider<HttpContext>
{
private HttpContext context;
private HttpRequest request;
private HttpResponse response;
/// <summary>HttpContext</summary>
public override HttpContext Context { get => context; }
/// <summary>创建服务程序实例。</summary>
/// <exception cref="ArgumentNullException"></exception>
public AspNetCoreProvider(HttpContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
this.context = context;
request = context.Request;
response = context.Response;
}
#region Request
/// <summary>获取 HTTP 方法。</summary>
public override Network.HttpMethod GetMethod() => ApiUtility.Method(request.Method);
/// <summary>获取客户端的 IP 地址。</summary>
public override string GetClientIP() => request.HttpContext.Connection.RemoteIpAddress.ToString();
/// <summary>获取请求的 URL。</summary>
public override Uri GetUrl()
{
var https = request.IsHttps;
var port = Context.Connection.LocalPort;
var query = request.QueryString == null ? null : request.QueryString.Value;
var sb = new StringBuilder();
sb.Append(https ? "https://" : "http://");
sb.Append(request.Host.Host ?? "");
if ((https && port != 443) || (!https && port != 80))
{
sb.Append(":");
sb.Append(port);
}
sb.Append(request.Path);
if (!string.IsNullOrEmpty(query))
{
if (!query.StartsWith("?")) sb.Append("?");
sb.Append(query);
}
var url = sb.ToString();
var uri = new Uri(url);
return uri;
}
/// <summary>获取请求的 Referrer。</summary>
public override string GetReferrer() => null;
/// <summary>获取请求的头。</summary>
public override HttpHeaders GetHeaders()
{
var headers = request.Headers;
var result = new HttpHeaders();
if (headers == null) return result;
foreach (var key in headers.Keys)
{
if (string.IsNullOrEmpty(key)) continue;
try
{
var value = headers[key];
if (string.IsNullOrEmpty(value)) continue;
result.Add(key, value);
}
catch { }
}
return result;
}
/// <summary>获取请求的内容类型。</summary>
public override string GetContentType() => request.ContentType;
/// <summary>获取请求的内容长度。</summary>
public override long GetContentLength() => request.ContentLength ?? -1L;
/// <summary>获取请求的内容。</summary>
public override Stream RequestBody() => request.Body;
#endregion
#region Response
/// <summary>设置 HTTP 状态。</summary>
public override void SetStatus(int status, int subStatus = 0) => response.StatusCode = status;
/// <summary>设置响应的头。</summary>
public override string SetHeader(string name, string value)
{
try
{
response.Headers.Add(name, value);
}
catch (Exception ex)
{
Logger.Internals.Error(nameof(AspNetCoreProvider), nameof(SetHeader), ex.Message(), name, value);
return ex.Message;
}
return null;
}
/// <summary>设置响应的缓存秒数,设置为 0 即不允许缓存。</summary>
public override void SetCache(int seconds)
{
if (seconds > 0)
{
SetHeader("Cache-Control", $"public, max-age={seconds}, s-maxage={seconds}");
}
else
{
SetHeader("Cache-Control", "no-cache, no-store, must-revalidate");
SetHeader("Pragma", "no-cache");
}
}
/// <summary>设置响应的缓存秒数。</summary>
public override void SetContentType(string value) => response.ContentType = value;
/// <summary>设置响应的内容长度。</summary>
public override void SetContentLength(long value) => response.ContentLength = value;
/// <summary>设置响应重定向。</summary>
public override void SetRedirect(string location) => SetRedirect(location, false);
/// <summary>获取 Response 流。</summary>
public override Stream ResponseBody() => response.Body;
#endregion
#region This
/// <summary>将客户端重新定向到新 URL。</summary>
/// <remarks>默认响应 302 状态。可指定 permanent = true 以响应 301 状态。</remarks>
public void SetRedirect(string location, bool permanent = false)
{
try { response.Redirect(location, permanent); } catch { }
}
private static void Async(Stream source, Stream destination, Func<long, bool> progress = null, int buffer = 4096, Action after = null)
{
if (source == null || !source.CanRead || destination == null || !destination.CanWrite)
{
after?.Invoke();
return;
}
Async(source, destination, progress, buffer, after, 0L);
}
private static void Async(Stream source, Stream destination, Func<long, bool> progress, int buffer, Action after, long total)
{
// 缓冲区。
var limit = buffer < 1 ? 1 : buffer;
var temp = new byte[limit];
// 读取一次。
try
{
source.BeginRead(temp, 0, limit, (ar1) =>
{
var count = source.EndRead(ar1);
if (count > 0)
{
destination.BeginWrite(temp, 0, count, (ar2) =>
{
destination.EndWrite(ar2);
total += count;
if (progress != null)
{
var @continue = progress.Invoke(total);
if (!@continue)
{
after?.Invoke();
return;
}
}
Async(source, destination, progress, buffer, after, total);
}, null);
}
else after?.Invoke();
}, null);
}
catch { }
}
/// <summary>解析 URL 的查询字符串,获取所有已解码的参数。</summary>
public static StringPairs ParseQuery(HttpRequest request)
{
if (request == null) return new StringPairs();
var sp = new StringPairs();
if (request.Query == null) return sp;
var keys = request.Query.Keys;
sp.Capacity = keys.Count;
foreach (var key in keys)
{
sp.Add(key, request.Query[key]);
}
sp.Capacity = sp.Count;
return sp;
}
#endregion
}
}
#endif