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 }); } }