using FreeRedis;
using Hangfire;
using Masuit.MyBlogs.Core.Common;
using Masuit.MyBlogs.Core.Configs;
using Masuit.MyBlogs.Core.Extensions.Firewall;
using Masuit.Tools.AspNetCore.Mime;
using Masuit.Tools.Core.Validator;
using Masuit.Tools.Logging;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Net.Http.Headers;
using System.Diagnostics;
using System.Text;
using System.Web;
using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
namespace Masuit.MyBlogs.Core.Controllers;
///
/// 错误页
///
[ApiExplorerSettings(IgnoreApi = true)]
public sealed class ErrorController : Controller
{
public IRedisClient RedisClient { get; set; }
///
/// 404
///
///
[Route("error"), Route("{*url}", Order = 99999), ResponseCache(Duration = 36000)]
public ActionResult Index()
{
Response.StatusCode = 404;
string accept = Request.Headers[HeaderNames.Accept] + "";
return true switch
{
_ when accept.StartsWith("image") => File("/Assets/images/404/4044.jpg", ContentType.Jpeg),
_ when Request.HasJsonContentType() || Request.Method == HttpMethods.Post => Json(new
{
StatusCode = 404,
Success = false,
Message = "页面未找到!"
}),
_ => View("Index")
};
}
///
/// 503
///
///
[Route("ServiceUnavailable")]
public async Task ServiceUnavailable()
{
var feature = HttpContext.Features.Get();
if (feature != null)
{
string err;
var ip = HttpContext.Connection.RemoteIpAddress;
switch (feature.Error)
{
case DbUpdateConcurrencyException ex:
err = $"数据库并发更新异常,更新表:{ex.Entries.Select(e => e.Metadata.Name)},请求路径({Request.Method}):{Request.Scheme}://{Request.Host}{HttpUtility.UrlDecode(feature.Path)}{Request.QueryString},客户端用户代理:{Request.Headers[HeaderNames.UserAgent]},客户端IP:{ip}\t{ex.InnerException?.Message},请求参数:\n{await GetRequestBody(Request)}\n堆栈信息:";
LogManager.Error(err, ex.Demystify());
break;
case DbUpdateException ex:
err = $"数据库更新时异常,更新表:{ex.Entries.Select(e => e.Metadata.Name)},请求路径({Request.Method}):{Request.Scheme}://{Request.Host}{HttpUtility.UrlDecode(feature.Path)}{Request.QueryString} ,客户端用户代理:{Request.Headers[HeaderNames.UserAgent]},客户端IP:{ip}\t{ex.InnerException?.Message},请求参数:\n{await GetRequestBody(Request)}\n堆栈信息:";
LogManager.Error(err, ex.Demystify());
break;
case AggregateException ex:
LogManager.Debug("↓↓↓" + ex.Message + "↓↓↓");
ex.Flatten().Handle(e =>
{
LogManager.Error($"异常源:{e.Source},异常类型:{e.GetType().Name},请求路径({Request.Method}):{Request.Scheme}://{Request.Host}{HttpUtility.UrlDecode(feature.Path)}{Request.QueryString} ,客户端用户代理:{Request.Headers[HeaderNames.UserAgent]},客户端IP:{ip}\t", e.Demystify());
return true;
});
var body = await GetRequestBody(Request);
if (!string.IsNullOrEmpty(body))
{
LogManager.Debug("↑↑↑请求参数:\n" + body);
}
break;
case AccessDenyException:
var entry = ip.GetIPLocation();
var tips = Template.Create(CommonHelper.SystemSettings.GetOrAdd("AccessDenyTips", @"遇到了什么问题?
基于主观因素考虑,您所在的地区暂时不允许访问本站,如有疑问,请联系站长!或者请联系站长开通本站的访问权限!
")).Set("clientip", ip.ToString()).Set("location", entry.Address).Set("network", entry.Network).Render();
Response.StatusCode = 403;
return View("AccessDeny", tips);
case TempDenyException:
Response.StatusCode = 429;
return Request.HasJsonContentType() || Request.Method == HttpMethods.Post ? Json(new
{
StatusCode = 429,
Success = false,
Message = $"检测到您的IP({ip})访问过于频繁,已被本站暂时禁止访问,请稍后再试!"
}) : View("TempDeny");
default:
LogManager.Error($"异常源:{feature.Error.Source},异常类型:{feature.Error.GetType().Name},请求路径({Request.Method}):{Request.Scheme}://{Request.Host}{HttpUtility.UrlDecode(feature.Path)}{Request.QueryString} ,客户端用户代理:{Request.Headers[HeaderNames.UserAgent]},客户端IP:{ip},请求参数:\n{await GetRequestBody(Request)}\n堆栈信息:", feature.Error.Demystify());
break;
}
}
Response.StatusCode = 503;
return Request.HasJsonContentType() || Request.Method == HttpMethods.Post ? Json(new
{
StatusCode = 503,
Success = false,
Message = "服务器发生错误!"
}) : View();
}
private static async Task GetRequestBody(HttpRequest req)
{
if (req.ContentLength > 5120)
{
return "请求体超长";
}
req.Body.Seek(0, SeekOrigin.Begin);
using var sr = new StreamReader(req.Body, Encoding.UTF8, false);
var body = await sr.ReadToEndAsync();
body = HttpUtility.UrlDecode(body);
req.Body.Position = 0;
return body;
}
///
/// 网站升级中
///
///
[Route("ComingSoon"), ResponseCache(Duration = 360000)]
public ActionResult ComingSoon()
{
return View();
}
///
/// 检查访问密码
///
///
///
///
[HttpPost, ValidateAntiForgeryToken, AllowAccessFirewall]
public ActionResult CheckViewToken(string email, string token)
{
if (string.IsNullOrEmpty(token))
{
return ResultData(null, false, "请输入访问密码!");
}
var s = RedisClient.Get("token:" + email);
if (!token.Equals(s))
{
return ResultData(null, false, "访问密码不正确!");
}
Response.Cookies.Append("Email", email, new CookieOptions
{
Expires = DateTime.Now.AddYears(1),
SameSite = SameSiteMode.Lax
});
Response.Cookies.Append("FullAccessToken", email.MDString(AppConfig.BaiduAK), new CookieOptions
{
Expires = DateTime.Now.AddYears(1),
SameSite = SameSiteMode.Lax
});
return ResultData(null);
}
///
/// 检查授权邮箱
///
///
///
///
[HttpPost, ValidateAntiForgeryToken, AllowAccessFirewall, ResponseCache(Duration = 100, VaryByQueryKeys = new[] { "email" })]
public ActionResult GetViewToken([FromServices] IUserInfoService userInfoService, string email)
{
var validator = new IsEmailAttribute();
if (!validator.IsValid(email))
{
return ResultData(null, false, validator.ErrorMessage);
}
if (RedisClient.Exists("get:" + email))
{
RedisClient.Expire("get:" + email, 120);
return ResultData(null, false, "发送频率限制,请在2分钟后重新尝试发送邮件!请检查你的邮件,若未收到,请检查你的邮箱地址或邮件垃圾箱!");
}
if (!userInfoService.Any(b => b.Email == email))
{
return ResultData(null, false, "您目前没有权限访问这个链接,请联系站长开通访问权限!");
}
var token = SnowFlake.GetInstance().GetUniqueShortId(6);
RedisClient.Set("token:" + email, token, 86400);
BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "博客访问验证码", $"{Request.Host}本次验证码是:{token},有效期为24h,请按时使用!", email, HttpContext.Connection.RemoteIpAddress.ToString()));
RedisClient.Set("get:" + email, token, 120);
return ResultData(null);
}
///
/// 响应数据
///
/// 数据
/// 响应状态
/// 响应消息
///
public ActionResult ResultData(object data, bool success = true, string message = "")
{
return Ok(new
{
Success = success,
Message = message,
Data = data
});
}
}