GroupView不要.cshtml后缀
智能大石头 编写于 2024-09-26 23:59:14
NewLife.Cube
using System.Reflection;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using NewLife.Cube.Extensions;
using NewLife.Log;
using NewLife.Model;
using NewLife.Remoting;
using NewLife.Serialization;
using XCode.Membership;
using IActionFilter = Microsoft.AspNetCore.Mvc.Filters.IActionFilter;

namespace NewLife.Cube;

/// <summary>控制器基类</summary>
[ApiController]
[Produces("application/json")]
[Route("[area]/[controller]/[action]")]
public class ControllerBaseX : ControllerBase, IActionFilter
{
    #region 属性
    /// <summary>临时会话扩展信息。仅限本地内存,不支持分布式共享</summary>
    public IDictionary<String, Object> Session { get; private set; }

    /// <summary>令牌</summary>
    public String Token { get; set; }

    /// <summary>当前页面菜单。用于权限控制</summary>
    public IMenu Menu { get; set; }

    /// <summary>当前用户</summary>
    public IManageUser CurrentUser { get; set; }

    /// <summary>当前租户</summary>
    public ITenantUser CurrentTenant { get; set; }

    /// <summary>用户主机</summary>
    public String UserHost => HttpContext.GetUserHost();

    /// <summary>页面设置</summary>
    public PageSetting PageSetting { get; set; }

    private IDictionary<String, Object> _args;
    #endregion

    #region 构造
    /// <summary>实例化控制器</summary>
    public ControllerBaseX() => PageSetting = PageSetting.Global.Clone();

    /// <summary>动作执行前</summary>
    /// <param name="context"></param>
    [NonAction]
    public virtual void OnActionExecuting(ActionExecutingContext context)
    {
        _args = context.ActionArguments;

        var ctx = HttpContext;
        Session = ctx.Items["Session"] as IDictionary<String, Object>;
        Menu = ctx.Items["CurrentMenu"] as IMenu;

        // 仅用于测试,跳过报错
        Session ??= new Dictionary<String, Object>();

        // 没有用户时无权
        var user = ManageProvider.User;
        if (user != null)
        {
            CurrentUser = user as IManageUser;

            // 设置变量,数据权限使用
            HttpContext.Items["userId"] = user.ID;

            // 没有菜单时不做权限控制
            //if (Menu != null)
            //{
            //    PageSetting.EnableSelect = user.Has(Menu, PermissionFlags.Update, PermissionFlags.Delete);
            //}
        }

        // 当前租户
        var tid = TenantContext.CurrentId;
        if (tid > 0)
        {
            var tus = TenantUser.FindAllByUserId(user.ID);
            CurrentTenant = tus.FirstOrDefault(e => e.TenantId == tid);
        }

        // 访问令牌
        Token = context.HttpContext.LoadToken();

        try
        {
            if (!Token.IsNullOrEmpty())
            {
            }

            if (user == null && context.ActionDescriptor is ControllerActionDescriptor act && !act.MethodInfo.IsDefined(typeof(AllowAnonymousAttribute)))
            {
                throw new ApiException(403, "认证失败");
            }
        }
        catch (Exception ex)
        {
            WriteLog(null, false, ex.ToString());

            context.Result = Json(0, null, ex);
        }
    }

    /// <summary>动作执行后</summary>
    /// <param name="context"></param>
    [NonAction]
    public virtual void OnActionExecuted(ActionExecutedContext context)
    {
        var ex = context.Exception?.GetTrue();
        if (ex != null && !context.ExceptionHandled)
        {
            WriteLog(null, false, ex.ToString());
        }

        var traceId = DefaultSpan.Current?.TraceId;

        if (context.Result != null)
        {
            if (context.Result is ObjectResult obj)
            {
                if (obj.Value is IApiResponse response)
                {
                    context.Result = new ContentResult
                    {
                        Content = OnJsonSerialize(response),
                        ContentType = "application/json",
                        StatusCode = 200
                    };
                }
                else
                {
                    var rs = new { code = obj.StatusCode ?? 0, data = obj.Value, traceId };
                    context.Result = new ContentResult
                    {
                        Content = OnJsonSerialize(rs),
                        ContentType = "application/json",
                        StatusCode = 200
                    };
                }
            }
            else if (context.Result is EmptyResult)
            {
                context.Result = new JsonResult(new { code = 0, data = new { }, traceId });
            }
        }
        else if (context.Exception != null && !context.ExceptionHandled)
        {
            //var ex = context.Exception.GetTrue();
            if (ex is ApiException aex)
                context.Result = new JsonResult(new { code = aex.Code, data = aex.Message, traceId });
            else
                context.Result = new JsonResult(new { code = 500, data = ex.Message, traceId });

            context.ExceptionHandled = true;

            // 输出异常日志
            if (XTrace.Debug) XTrace.WriteException(ex);
        }
    }
    #endregion

    #region 兼容处理
    /// <summary>获取请求值</summary>
    /// <param name="key"></param>
    /// <returns></returns>
    protected virtual String GetRequest(String key) => Request.GetRequestValue(key);
    #endregion

    #region Json结果
    /// <summary>响应Json结果</summary>
    /// <param name="code">代码。0成功,其它为错误代码</param>
    /// <param name="message">消息,成功或失败时的文本消息</param>
    /// <param name="data">数据对象</param>
    /// <param name="extend">扩展数据</param>
    /// <returns></returns>
    [NonAction]
    public virtual ActionResult Json(Int32 code, String message, Object data = null, Object extend = null)
    {
        if (data is Exception ex)
        {
            if (code == 0 && data is ApiException aex) code = aex.Code;
            if (code == 0) code = 500;
            if (message.IsNullOrEmpty()) message = ex.GetTrue()?.Message;
            data = null;
        }

        Object rs = new { code, message, data };
        if (extend != null)
        {
            var dic = rs.ToDictionary();
            dic.Merge(extend);
            rs = dic;
        }

        var json = OnJsonSerialize(rs);
        DefaultSpan.Current?.AppendTag(json);

        return Content(json, "application/json", Encoding.UTF8);
    }

    /// <summary>Json序列化。默认使用FastJson</summary>
    /// <param name="data"></param>
    /// <returns></returns>
    protected virtual String OnJsonSerialize(Object data)
    {
        //data.ToJson(false, true, true);
        var writer = new JsonWriter
        {
            //Indented = false,
            //IgnoreNullValues = false,
            //CamelCase = true,
            Int64AsString = true
        };
        writer.Options.WriteIndented = false;
        writer.Options.IgnoreNullValues = false;
        writer.Options.CamelCase = true;

        writer.Write(data);

        return writer.GetString();
    }
    #endregion

    #region 辅助
    /// <summary>写审计日志</summary>
    /// <param name="action"></param>
    /// <param name="success"></param>
    /// <param name="remark"></param>
    protected virtual void WriteLog(String action, Boolean success, String remark)
    {
        action ??= GetControllerAction().LastOrDefault();

        var type = GetType();
        if (type.BaseType.IsGenericType) type = type.BaseType.GetGenericArguments().FirstOrDefault();

        LogProvider.Provider?.WriteLog(type, action, success, remark, 0, null, UserHost);
    }

    /// <summary>获取控制器名称,返回 area, controller, action</summary>
    /// <returns></returns>
    protected virtual String[] GetControllerAction()
    {
        var act = ControllerContext.ActionDescriptor;
        var controller = act.ControllerName;
        var action = act.ActionName;
        act.RouteValues.TryGetValue("Area", out var area);

        return new[] { area, controller, action };
    }
    #endregion
}