博客 (29)

在 .NET Framework 的缓存管理中,cacheMemoryLimitMegabytes 是一个关键配置属性,用于控制内存缓存(MemoryCache)实例的最大内存占用。以下是其具体用法及实现细节:

  1. 基本定义与作用

    • 功能:通过 cacheMemoryLimitMegabytes 可设置 MemoryCache 实例允许占用的最大内存(单位:MB)。若缓存数据超过此限制,系统会自动淘汰旧条目。

    • 默认值:默认值为 0,表示缓存基于计算机的物理内存自动管理(例如根据可用内存动态调整)。

  2. 配置方式

    • 通过配置文件(web.config)

      在 web.config 的 <system.runtime.caching> 节点下配置 namedCaches,示例:

      <configuration>
        <system.runtime.caching>
          <memoryCache>
            <namedCaches>
              <add 
                name="Default" 
                cacheMemoryLimitMegabytes="500" 
                physicalMemoryLimitPercentage="50" 
                pollingInterval="00:05:00" />
            </namedCaches>
          </memoryCache>
        </system.runtime.caching>
      </configuration>

      参数说明:

      • cacheMemoryLimitMegabytes:最大内存限制(例如 500 表示 500MB)。

      • physicalMemoryLimitPercentage:允许使用的物理内存百分比(可选)。

      • pollingInterval:缓存清理策略的轮询间隔(例如每5分钟检查一次)。

    • 通过代码动态配置

      在初始化 MemoryCache 时,通过 NameValueCollection 传递参数:

      var config = new NameValueCollection
      {
          { "cacheMemoryLimitMegabytes", "500" },
          { "physicalMemoryLimitPercentage", "50" },
          { "pollingInterval", "00:05:00" }
      };
      var cache = new MemoryCache("CustomCache", config);

      此方式适用于需要动态调整缓存策略的场景。

  3. 注意事项

    • 优先级规则:

      若同时配置了 cacheMemoryLimitMegabytes 和 physicalMemoryLimitPercentage,系统会选择两者中较小的值作为限制。

    • 分布式缓存兼容性:

      此属性仅适用于进程内缓存(如 MemoryCache),若使用 Redis 等分布式缓存需通过其独立配置管理内存。

    • 监控与调试:

      建议结合性能计数器(如 ASP.NET Applications 类别下的 Cache Total Entries)或日志记录模块(参考 web.config 的 <system.diagnostics> 配置)监控实际内存占用。

  4. 应用场景示例

    场景:一个电商网站需要缓存商品目录数据,限制最大内存为 1GB。

    配置实现:

    <add 
      name="ProductCatalogCache" 
      cacheMemoryLimitMegabytes="1024" 
      pollingInterval="00:10:00" />

    代码调用:

    var productCatalog = MemoryCache.Default["ProductCatalog"];
  5. 常见问题

    1. Q:设置为 0 时缓存会无限制增长吗?

      A:不会。此时缓存基于系统物理内存动态管理,通常上限为总内存的 70%-90%。

    2. Q:如何验证配置已生效?

      A:可通过 MemoryCache.GetCount() 统计条目数量,或使用性能监视器跟踪内存占用。

xoyozo 25 天前
221

SignalR 是一个开源的实时通信库,用于构建实时 Web 应用程序。它提供了一个简单的 API,可以在客户端和服务器之间建立持久连接,以便实时地推送数据。

与传统的 WebSocket 相比,SignalR 提供了更高级的功能和更简单的开发体验。下面是一些主要区别:

  • 支持多种传输方式:SignalR 可以使用多种传输方式,包括 WebSocket、Server-Sent Events(SSE)、长轮询和 Forever Frame。这使得 SignalR 在不同的环境中都能提供实时通信的能力,即使某些浏览器不支持 WebSocket,也可以使用其他传输方式。

  • 自动处理连接管理:SignalR 管理连接的生命周期,包括连接的建立、断开和重新连接。它会自动处理连接的失败和重新连接的逻辑,简化了开发人员的工作。

  • 服务器端推送:SignalR 允许服务器端主动推送消息给客户端,而不需要客户端发起请求。这使得实时通信变得更加高效和实时,适用于聊天应用、实时监控等场景。

  • 跨平台支持:SignalR 可以在多个平台上使用,包括 .NET、Java、JavaScript 等。这使得开发人员可以使用自己熟悉的语言和框架来构建实时应用程序。


微软官方提供了针对 ASP.NET Core Web 应用(Razor 页面)的详细教程,这里给出 MVC 版本入门教程。


最终将创建一个正常运行的聊天应用:

signalr-get-started-finished.png


创建 Web 应用项目

image.png

image.png


添加 SignalR 客户端库

在“解决方案资源管理器”>中,右键单击项目,然后选择“添加”“客户端库”。

在“添加客户端库”对话框中:

  • “提供程序”选择“unpkg”

  • “库”,请输入 @microsoft/signalr@latest。

  • 选择“选择特定文件”,展开“dist/browser”文件夹,然后选择 signalr.js 和 signalr.min.js。

  • 点击“安装” 。

image.png


创建 SignalR Hubs 类

using Microsoft.AspNetCore.SignalR;

/// <summary>
/// Hub 类管理连接、组和消息
/// </summary>
public class ChatHub : Hub
{
        /// <summary>
        /// 可通过已连接客户端调用 SendMessage,以向所有客户端发送消息
        /// </summary>
    public async Task SendMessage(string user, string message)
    {
        // Clients.All 向所有的客户端发送消息
        // ReceiveMessage 是客户端监听的方法
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

其父类 Hub 可管理连接、组和消息。这里演示的是向所有客户端发送消息。


配置 SignalR

打开 Program.cs,添加注入:

builder.Services.AddSignalR();

添加路由:

app.MapHub<ChatHub>("/chatHub");


添加 SignalR 客户端代码

视图页面:

<div class="container">
    <div class="row p-1">
        <div class="col-1">用户</div>
        <div class="col-5"><input type="text" id="userInput" /></div>
    </div>
    <div class="row p-1">
        <div class="col-1">消息</div>
        <div class="col-5"><input type="text" class="w-100" id="messageInput" /></div>
    </div>
    <div class="row p-1">
        <div class="col-6 text-end">
            <input type="button" id="sendButton" value="发送消息" />
        </div>
    </div>
    <div class="row p-1">
        <div class="col-6">
            <hr />
        </div>
    </div>
    <div class="row p-1">
        <div class="col-6">
            <ul id="messagesList"></ul>
        </div>
    </div>
</div>
<script src="~/lib/microsoft/signalr/dist/browser/signalr.min.js"></script>
<script src="~/js/chat.js"></script>

chat.js 文件:

"use strict";

var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

// 在连接建立之前禁用发送按钮
document.getElementById("sendButton").disabled = true;

connection.on("ReceiveMessage", function (user, message) {
    var li = document.createElement("li");
    document.getElementById("messagesList").appendChild(li);
    // 修改此处时应注意脚本注入问题
    li.textContent = `${user} says ${message}`;
});

connection.start().then(function () {
    document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
    return console.error(err.toString());
});

document.getElementById("sendButton").addEventListener("click", function (event) {
    var user = document.getElementById("userInput").value;
    var message = document.getElementById("messageInput").value;
    connection.invoke("SendMessage", user, message).catch(function (err) {
        return console.error(err.toString());
    });
    event.preventDefault();
});


完成。在线示例:https://xoyozo.net/Demo/SignalRDemo






xoyozo 1 年前
1,943
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace xxx.xxx.xxx.Controllers
{
    public class DiscuzTinyintViewerController : Controller
    {
        public IActionResult Index()
        {
            using var context = new Data.xxx.xxxContext();

            var conn = context.Database.GetDbConnection();
            conn.Open();
            using var cmd = conn.CreateCommand();
            cmd.CommandText = "SELECT `TABLE_NAME` FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE();";
            Dictionary<string, List<FieldType>?> tables = new();

            using var r = cmd.ExecuteReader();
            while (r.Read())
            {
                tables.Add((string)r["TABLE_NAME"], null);
            }
            conn.Close();

            foreach (var table in tables)
            {
                var conn2 = context.Database.GetDbConnection();
                conn2.Open();
                using var cmd2 = conn2.CreateCommand();
                cmd2.CommandText = "DESCRIBE " + table.Key;
                using var r2 = cmd2.ExecuteReader();
                List<FieldType> fields = new();
                while (r2.Read())
                {
                    if (((string)r2[1]).Contains("tinyint(1)"))
                    {
                        fields.Add(new()
                        {
                            Field = (string)r2[0],
                            Type = (string)r2[1],
                            Null = (string)r2[2],
                        });
                    }
                }
                conn2.Close();
                tables[table.Key] = fields;
            }

            foreach (var table in tables)
            {
                foreach (var f in table.Value)
                {
                    var conn3 = context.Database.GetDbConnection();
                    conn3.Open();
                    using var cmd3 = conn3.CreateCommand();
                    cmd3.CommandText = $"SELECT {f.Field} as F, COUNT({f.Field}) as C FROM {table.Key} GROUP BY {f.Field}";
                    using var r3 = cmd3.ExecuteReader();
                    List<FieldType.ValueCount> vs = new();
                    while (r3.Read())
                    {
                        vs.Add(new() { Value = Convert.ToString(r3["F"]), Count = Convert.ToInt32(r3["C"]) });
                    }
                    conn3.Close();
                    f.groupedValuesCount = vs;
                }
            }

            return Json(tables.Where(c => c.Value != null && c.Value.Count > 0));
        }

        private class FieldType
        {
            public string Field { get; set; }
            public string Type { get; set; }
            public string Null { get; set; }
            public List<ValueCount> groupedValuesCount { get; set; }
            public class ValueCount
            {
                public string Value { get; set; }
                public int Count { get; set; }
            }
            public string RecommendedType
            {
                get
                {
                    if (groupedValuesCount == null || groupedValuesCount.Count < 2)
                    {
                        return "无建议";
                    }
                    else if (groupedValuesCount.Count == 2 && groupedValuesCount.Any(c => c.Value == "0") && groupedValuesCount.Any(c => c.Value == "1"))
                    {
                        return "bool" + (Null == "YES" ? "?" : "");
                    }
                    else
                    {
                        return "sbyte" + (Null == "YES" ? "?" : "");
                    }
                }
            }
        }
    }
}
[{
	"key": "pre_forum_post",
	"value": [{
		"field": "first",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "0",
			"count": 1395501
		}, {
			"value": "1",
			"count": 179216
		}],
		"recommendedType": "bool"
	}, {
		"field": "invisible",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "-5",
			"count": 9457
		}, {
			"value": "-3",
			"count": 1412
		}, {
			"value": "-2",
			"count": 1122
		}, {
			"value": "-1",
			"count": 402415
		}, {
			"value": "0",
			"count": 1160308
		}, {
			"value": "1",
			"count": 3
		}],
		"recommendedType": "sbyte"
	}, {
		"field": "anonymous",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "0",
			"count": 1574690
		}, {
			"value": "1",
			"count": 27
		}],
		"recommendedType": "bool"
	}, {
		"field": "usesig",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "0",
			"count": 162487
		}, {
			"value": "1",
			"count": 1412230
		}],
		"recommendedType": "bool"
	}, {
		"field": "htmlon",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "0",
			"count": 1574622
		}, {
			"value": "1",
			"count": 95
		}],
		"recommendedType": "bool"
	}, {
		"field": "bbcodeoff",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "-1",
			"count": 935448
		}, {
			"value": "0",
			"count": 639229
		}, {
			"value": "1",
			"count": 40
		}],
		"recommendedType": "sbyte"
	}, {
		"field": "smileyoff",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "-1",
			"count": 1359482
		}, {
			"value": "0",
			"count": 215186
		}, {
			"value": "1",
			"count": 49
		}],
		"recommendedType": "sbyte"
	}, {
		"field": "parseurloff",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "0",
			"count": 1572844
		}, {
			"value": "1",
			"count": 1873
		}],
		"recommendedType": "bool"
	}, {
		"field": "attachment",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "0",
			"count": 1535635
		}, {
			"value": "1",
			"count": 2485
		}, {
			"value": "2",
			"count": 36597
		}],
		"recommendedType": "sbyte"
	}, {
		"field": "comment",
		"type": "tinyint(1)",
		"null": "NO",
		"groupedValuesCount": [{
			"value": "0",
			"count": 1569146
		}, {
			"value": "1",
			"count": 5571
		}],
		"recommendedType": "bool"
	}]
}]


xoyozo 3 年前
1,989

命名空间“X.PagedList.Mvc.Core”中不存在类型或命名空间名“Common”(是否缺少程序集引用?)

X.PagedList.Mvc.Core.Common.PagedListRenderOptions

X.PagedList.Web.Common.PagedListRenderOptionsBase

改为

X.PagedList.Web.Common.PagedListRenderOptions

xoyozo 4 年前
2,843

fail: Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor[3]

      The view 'Index' was not found. Searched locations: /Areas/AAA/Views/XXX/Index.cshtml, /Areas/AAA/Views/Shared/Index.cshtml, /Views/Shared/Index.cshtml

fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]

      An unhandled exception has occurred while executing the request.

      System.InvalidOperationException: The view 'Index' was not found. The following locations were searched:

      /Areas/AAA/Views/XXX/Index.cshtml

      /Areas/AAA/Views/Shared/Index.cshtml

      /Views/Shared/Index.cshtml


开发环境一切正常,发布到服务器上出现上述错误,找不到视图文件,可视图文件明明就存在。

尝试新建一个文件,将 Index.cshtml 文件的内容拷贝到新文件中保存,删除原文件,将新文件改名为 Index.cshtml,发布,OK。

xoyozo 4 年前
3,487

ASP.NET MVC 使用 Authorize 过滤器验证用户登录。Authorize 过滤器首先运行在任何其它过滤器或动作方法之前,主要用来做登录验证或者权限验证。

示例:使用 Authorize 过滤器实现简单的用户登录验证。

1、创建登录控制器 LoginController

/// <summary>
/// 登录控制器
/// </summary>
[AllowAnonymous]
public class LoginController : Controller
{
    /// <summary>
    /// 登录页面
    /// </summary>
    public ActionResult Index()
    {
        return View();
    }
 
    /// <summary>
    /// 登录
    /// </summary>
    [HttpPost]
    public ActionResult Login(string loginName, string loginPwd)
    {
        if (loginName == "admin" && loginPwd == "123456")
        {
            // 登录成功
            Session["LoginName"] = loginName;
            return RedirectToAction("Index", "Home");
        }
        else
        {
            // 登录失败
            return RedirectToAction("Index", "Login");
        }
    }
 
    /// <summary>
    /// 注销
    /// </summary>
    public ActionResult Logout()
    {
        Session.Abandon();
        return RedirectToAction("Index", "Login");
    }
}

注意:在登录控制器 LoginController 上添加 AllowAnonymous 特性,该特性用于标记在授权期间要跳过 AuthorizeAttribute 的控制器和操作。

2、创建登录页面

@{
    ViewBag.Title = "登录页面";
    Layout = null;
}
 
<h2>登录页面</h2>
 
<form action='@Url.Action("Login","Login")' id="form1" method="post">
    用户:<input type="text" name="loginName" /><br />
    密码:<input type="password" name="loginPwd" /><br />
    <input type="submit" value="登录">
</form>

效果图:

3、创建主页控制器 LoginController

public class HomeController : Controller
{
    public ActionResult Index()
    {
        // 获取当前登录用户
        string loginName = Session["LoginName"].ToString();
        ViewBag.Message = "当前登录用户:" + loginName;
        return View();
    }
}

4、创建主页页面

@{
    ViewBag.Title = "Index";
    Layout = null;
}
 
<h2>Index</h2>
<h3>@ViewBag.Message</h3>
<a href="@Url.Action("Logout","Login")">注销</a>

效果图:

5、创建授权过滤器 LoginAuthorizeAttribute 类

创建 Filter 目录,在该目录下创建授权过滤器 LoginAuthorizeAttribute 类,继承 AuthorizeAttribute。

using System.Web.Mvc;
 
namespace MvcApp.Filter
{
    /// <summary>
    /// 授权过滤器
    /// </summary>
    public class LoginAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            // 判断是否跳过授权过滤器
            if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)
               || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
            {
                return;
            }
 
            // 判断登录情况
            if (filterContext.HttpContext.Session["LoginName"] == null || filterContext.HttpContext.Session["LoginName"].ToString()=="")
            {
                //HttpContext.Current.Response.Write("认证不通过");
                //HttpContext.Current.Response.End();
 
                filterContext.Result = new RedirectResult("/Login/Index");
            }
        }
    }
}

通常 Authorize 过滤器也是在全局过滤器上面的,在 App_Start 目录下的 FilterConfig 类的 RegisterGlobalFilters 方法中添加:

using System.Web;
using System.Web.Mvc;
using MvcApp.Filter;
 
namespace MvcApp
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
 
            // 添加全局授权过滤器
            filters.Add(new LoginAuthorizeAttribute());
        }
    }
}

Global.asax 下的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
 
namespace MvcApp
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}


xoyozo:暂时没有按区域(Area)来统一配置的方法,建议加在 Controller 上。https://stackoverflow.com/questions/2319157/how-can-we-set-authorization-for-a-whole-area-in-asp-net-mvc


p
转自 pan_junbiao 5 年前
4,366

form 提交 checkbox

<form method="get" asp-action="Index">
    <input type="checkbox" name="a" value="1" /> 一
    <input type="checkbox" name="a" value="2" /> 二
</form>

get 方式的 url 中会出现多个相同名称的参数

/abc?a=1&a=2

C# 使用数组类型接收参数

public IActionResult Index(string[] a)
{
}


xoyozo 5 年前
3,721
返回状态ASP.NET MVCASP.NET CoreASP.NET Web API
200 视图return View();return View();-
200 带模型的视图return View(Model);return View(Model);-
200 空内容return new EmptyResult();return Ok();return Ok();
200 文本
return Content(string);return Ok(string);
return Content(string);
return Ok(string);
return Content(string);
200 JSONreturn Json(object);

return Json(T); // 强制 JSON

return Ok(T); // 请求时可指定 Content-Type

return Json(T); // 强制 JSON

return Ok(T); // 请求时可指定 Content-Type

200 JaveScriptreturn JavaScript(string script);

200 文件return File();

return File();

return PhysicalFile();

return File();

return PhysicalFile();

201 已创建

return Created(location, content);

用于 Post 接口

301 永久跳转

return RedirectPermanent(url);

return RedirectToActionPermanent(url);

return RedirectToRoutePermanent(object);

return RedirectPermanent(url);

return RedirectToActionPermanent(url);

return RedirectToRoutePermanent(object);


302 临时跳转

return Redirect(url);

return RedirectToAction(url);

return RedirectToRoute(object);

return Redirect(url);

return RedirectToAction(url);

return RedirectToRoute(object);

return Redirect(url);

return RedirectToRoute(object);

400 错误的请求return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest);return BadRequest(msg);return BadRequest(msg);
401 未授权return new HttpUnauthorizedResult("描述");return Unauthorized(msg);return Unauthorized(msg);
404 找不到文件或目录return HttpNotFound(); return NotFound(msg);return NotFound(msg);
直接指定状态码

return new HttpStatusCodeResult(HttpStatusCode.*);


public enum HttpStatusCode

{

//

// 摘要:

//     等效于 HTTP 状态 100。 System.Net.HttpStatusCode.Continue 指示客户端可能继续其请求。

Continue = 100,

//

// 摘要:

//     等效于 HTTP 状态 101。 System.Net.HttpStatusCode.SwitchingProtocols 指示正在更改协议版本或协议。

SwitchingProtocols = 101,

//

// 摘要:

//     等效于 HTTP 状态 200。 System.Net.HttpStatusCode.OK 指示请求成功,且请求的信息包含在响应中。 这是最常接收的状态代码。

OK = 200,

//

// 摘要:

//     等效于 HTTP 状态 201。 System.Net.HttpStatusCode.Created 指示请求导致在响应被发送前创建新资源。

Created = 201,

//

// 摘要:

//     等效于 HTTP 状态 202。 System.Net.HttpStatusCode.Accepted 指示已接受请求做进一步处理。

Accepted = 202,

//

// 摘要:

//     等效于 HTTP 状态 203。 System.Net.HttpStatusCode.NonAuthoritativeInformation 指示返回的元信息来自缓存副本而不是原始服务器,因此可能不正确。

NonAuthoritativeInformation = 203,

//

// 摘要:

//     等效于 HTTP 状态 204。 System.Net.HttpStatusCode.NoContent 指示已成功处理请求并且响应已被设定为无内容。

NoContent = 204,

//

// 摘要:

//     等效于 HTTP 状态 205。 System.Net.HttpStatusCode.ResetContent 指示客户端应重置(而非重新加载)当前资源。

ResetContent = 205,

//

// 摘要:

//     等效于 HTTP 状态 206。 System.Net.HttpStatusCode.PartialContent 指示响应是包括字节范围的 GET 请求所请求的部分响应。

PartialContent = 206,

//

// 摘要:

//     等效于 HTTP 状态 300。 System.Net.HttpStatusCode.MultipleChoices 指示请求的信息有多种表示形式。 默认操作是将此状态视为重定向,并遵循与此响应关联的

//     Location 标头的内容。

MultipleChoices = 300,

//

// 摘要:

//     等效于 HTTP 状态 300。 System.Net.HttpStatusCode.Ambiguous 指示请求的信息有多种表示形式。 默认操作是将此状态视为重定向,并遵循与此响应关联的

//     Location 标头的内容。

Ambiguous = 300,

//

// 摘要:

//     等效于 HTTP 状态 301。 System.Net.HttpStatusCode.MovedPermanently 指示请求的信息已移到 Location

//     头中指定的 URI 处。 接收到此状态时的默认操作为遵循与响应关联的 Location 标头。

MovedPermanently = 301,

//

// 摘要:

//     等效于 HTTP 状态 301。 System.Net.HttpStatusCode.Moved 指示请求的信息已移到 Location 头中指定的 URI

//     处。 接收到此状态时的默认操作为遵循与响应关联的 Location 标头。 原始请求方法为 POST 时,重定向的请求将使用 GET 方法。

Moved = 301,

//

// 摘要:

//     等效于 HTTP 状态 302。 System.Net.HttpStatusCode.Found 指示请求的信息位于 Location 标头中指定的 URI

//     处。 接收到此状态时的默认操作为遵循与响应关联的 Location 标头。 原始请求方法为 POST 时,重定向的请求将使用 GET 方法。

Found = 302,

//

// 摘要:

//     等效于 HTTP 状态 302。 System.Net.HttpStatusCode.Redirect 指示请求的信息位于 Location 标头中指定的

//     URI 处。 接收到此状态时的默认操作为遵循与响应关联的 Location 标头。 原始请求方法为 POST 时,重定向的请求将使用 GET 方法。

Redirect = 302,

//

// 摘要:

//     等效于 HTTP 状态 303。 作为 POST 的结果,System.Net.HttpStatusCode.SeeOther 将客户端自动重定向到 Location

//     标头中指定的 URI。 用 GET 生成对 Location 标头所指定的资源的请求。

SeeOther = 303,

//

// 摘要:

//     等效于 HTTP 状态 303。 作为 POST 的结果,System.Net.HttpStatusCode.RedirectMethod 将客户端自动重定向到

//     Location 标头中指定的 URI。 用 GET 生成对 Location 标头所指定的资源的请求。

RedirectMethod = 303,

//

// 摘要:

//     等效于 HTTP 状态 304。 System.Net.HttpStatusCode.NotModified 指示客户端的缓存副本是最新的。 未传输此资源的内容。

NotModified = 304,

//

// 摘要:

//     等效于 HTTP 状态 305。 System.Net.HttpStatusCode.UseProxy 指示请求应使用位于 Location 标头中指定的

//     URI 的代理服务器。

UseProxy = 305,

//

// 摘要:

//     等效于 HTTP 状态 306。 System.Net.HttpStatusCode.Unused 是未完全指定的 HTTP/1.1 规范的建议扩展。

Unused = 306,

//

// 摘要:

//     等效于 HTTP 状态 307。 System.Net.HttpStatusCode.TemporaryRedirect 指示请求信息位于 Location

//     标头中指定的 URI 处。 接收到此状态时的默认操作为遵循与响应关联的 Location 标头。 原始请求方法为 POST 时,重定向的请求还将使用 POST

//     方法。

TemporaryRedirect = 307,

//

// 摘要:

//     等效于 HTTP 状态 307。 System.Net.HttpStatusCode.RedirectKeepVerb 指示请求信息位于 Location

//     标头中指定的 URI 处。 接收到此状态时的默认操作为遵循与响应关联的 Location 标头。 原始请求方法为 POST 时,重定向的请求还将使用 POST

//     方法。

RedirectKeepVerb = 307,

//

// 摘要:

//     等效于 HTTP 状态 400。 System.Net.HttpStatusCode.BadRequest 指示服务器未能识别请求。 如果没有其他适用的错误,或者不知道准确的错误或错误没有自己的错误代码,则发送

//     System.Net.HttpStatusCode.BadRequest。

BadRequest = 400,

//

// 摘要:

//     等效于 HTTP 状态 401。 System.Net.HttpStatusCode.Unauthorized 指示请求的资源要求身份验证。 WWW-Authenticate

//     标头包含如何执行身份验证的详细信息。

Unauthorized = 401,

//

// 摘要:

//     等效于 HTTP 状态 402。 保留 System.Net.HttpStatusCode.PaymentRequired 以供将来使用。

PaymentRequired = 402,

//

// 摘要:

//     等效于 HTTP 状态 403。 System.Net.HttpStatusCode.Forbidden 指示服务器拒绝满足请求。

Forbidden = 403,

//

// 摘要:

//     等效于 HTTP 状态 404。 System.Net.HttpStatusCode.NotFound 指示请求的资源不在服务器上。

NotFound = 404,

//

// 摘要:

//     等效于 HTTP 状态 405。 System.Net.HttpStatusCode.MethodNotAllowed 指示请求的资源上不允许请求方法(POST

//     或 GET)。

MethodNotAllowed = 405,

//

// 摘要:

//     等效于 HTTP 状态 406。 System.Net.HttpStatusCode.NotAcceptable 指示客户端已用 Accept 标头指示将不接受资源的任何可用表示形式。

NotAcceptable = 406,

//

// 摘要:

//     等效于 HTTP 状态 407。 System.Net.HttpStatusCode.ProxyAuthenticationRequired 指示请求的代理要求身份验证。

//     Proxy-authenticate 标头包含如何执行身份验证的详细信息。

ProxyAuthenticationRequired = 407,

//

// 摘要:

//     等效于 HTTP 状态 408。 System.Net.HttpStatusCode.RequestTimeout 指示客户端没有在服务器期望请求的时间内发送请求。

RequestTimeout = 408,

//

// 摘要:

//     等效于 HTTP 状态 409。 System.Net.HttpStatusCode.Conflict 指示由于服务器上的冲突而未能执行请求。

Conflict = 409,

//

// 摘要:

//     等效于 HTTP 状态 410。 System.Net.HttpStatusCode.Gone 指示请求的资源不再可用。

Gone = 410,

//

// 摘要:

//     等效于 HTTP 状态 411。 System.Net.HttpStatusCode.LengthRequired 指示缺少必需的 Content-length

//     标头。

LengthRequired = 411,

//

// 摘要:

//     等效于 HTTP 状态 412。 System.Net.HttpStatusCode.PreconditionFailed 指示为此请求设置的条件失败,且无法执行此请求。条件是用条件请求标头(如

//     If-Match、If-None-Match 或 If-Unmodified-Since)设置的。

PreconditionFailed = 412,

//

// 摘要:

//     等效于 HTTP 状态 413。 System.Net.HttpStatusCode.RequestEntityTooLarge 指示请求太大,服务器无法处理。

RequestEntityTooLarge = 413,

//

// 摘要:

//     等效于 HTTP 状态 414。 System.Net.HttpStatusCode.RequestUriTooLong 指示 URI 太长。

RequestUriTooLong = 414,

//

// 摘要:

//     等效于 HTTP 状态 415。 System.Net.HttpStatusCode.UnsupportedMediaType 指示请求是不受支持的类型。

UnsupportedMediaType = 415,

//

// 摘要:

//     等效于 HTTP 状态 416。 System.Net.HttpStatusCode.RequestedRangeNotSatisfiable 指示无法返回从资源请求的数据范围,因为范围的开头在资源的开头之前,或因为范围的结尾在资源的结尾之后。

RequestedRangeNotSatisfiable = 416,

//

// 摘要:

//     等效于 HTTP 状态 417。 System.Net.HttpStatusCode.ExpectationFailed 指示服务器未能符合 Expect

//     标头中给定的预期值。

ExpectationFailed = 417,

//

// 摘要:

//     等效于 HTTP 状态 426。 System.Net.HttpStatusCode.UpgradeRequired 指示客户端应切换为诸如 TLS/1.0

//     之类的其他协议。

UpgradeRequired = 426,

//

// 摘要:

//     等效于 HTTP 状态 500。 System.Net.HttpStatusCode.InternalServerError 指示服务器上发生了一般错误。

InternalServerError = 500,

//

// 摘要:

//     等效于 HTTP 状态 501。 System.Net.HttpStatusCode.NotImplemented 指示服务器不支持请求的函数。

NotImplemented = 501,

//

// 摘要:

//     等效于 HTTP 状态 502。 System.Net.HttpStatusCode.BadGateway 指示中间代理服务器从另一代理或原始服务器接收到错误响应。

BadGateway = 502,

//

// 摘要:

//     等效于 HTTP 状态 503。 System.Net.HttpStatusCode.ServiceUnavailable 指示服务器暂时不可用,通常是由于过多加载或维护。

ServiceUnavailable = 503,

//

// 摘要:

//     等效于 HTTP 状态 504。 System.Net.HttpStatusCode.GatewayTimeout 指示中间代理服务器在等待来自另一个代理或原始服务器的响应时已超时。

GatewayTimeout = 504,

//

// 摘要:

//     等效于 HTTP 状态 505。 System.Net.HttpStatusCode.HttpVersionNotSupported 指示服务器不支持请求的

//     HTTP 版本。

HttpVersionNotSupported = 505

}




附 ASP.NET Form 相关:

400:

Response.StatusCode = 400;
Response.Write("错误提示");
Response.End();


xoyozo 5 年前
4,126

Nuget 安装:X.PagedList.Mvc.Core


控制器:

using X.PagedList;

public IActionResult Index(int page = 1)
{
    ……
    return View(q.ToPagedList(page, size));
}


视图:

@using X.PagedList
@using X.PagedList.Mvc.Core
@model IPagedList<xxx>

@Html.PagedListPager(Model, page => Url.Action("Index", new { page }))


自定义(options 默认值):

@Html.PagedListPager(
    Model, 
    page => Url.Action("Index", new { page }), 
    new X.PagedList.Mvc.Common.PagedListRenderOptionsBase
    {
        HtmlEncoder = HtmlEncoder.get_Default(),
        DisplayLinkToFirstPage = PagedListDisplayMode.IfNeeded,
        DisplayLinkToLastPage = PagedListDisplayMode.IfNeeded,
        DisplayLinkToPreviousPage = PagedListDisplayMode.IfNeeded,
        DisplayLinkToNextPage = PagedListDisplayMode.IfNeeded,
        DisplayLinkToIndividualPages = true,
        DisplayPageCountAndCurrentLocation = false, // 显示总页数和当前页码
        MaximumPageNumbersToDisplay = 10, // 最多显示页码数
        DisplayEllipsesWhenNotShowingAllPageNumbers = true,
        EllipsesFormat = "&#8230;",
        LinkToFirstPageFormat = "<<",
        LinkToPreviousPageFormat = "<",
        LinkToIndividualPageFormat = "{0}",
        LinkToNextPageFormat = ">",
        LinkToLastPageFormat = ">>",
        PageCountAndCurrentLocationFormat = "Page {0} of {1}.",
        ItemSliceAndTotalFormat = "Showing items {0} through {1} of {2}.",
        FunctionToDisplayEachPageNumber = null,
        ClassToApplyToFirstListItemInPager = null,
        ClassToApplyToLastListItemInPager = null,
        ContainerDivClasses = new string[1]
        {
            "pagination-container"
        },
        UlElementClasses = new string[1]
        {
            "pagination"
        },
        LiElementClasses = Enumerable.Empty<string>(),
        PageClasses = Enumerable.Empty<string>(),
        UlElementattributes = null,
        ActiveLiElementClass = "active",
        EllipsesElementClass = "PagedList-ellipses",
        PreviousElementClass = "PagedList-skipToPrevious",
        NextElementClass = "PagedList-skipToNext",
    })


保留地址栏参数:

@{ 
    string query = Context.Request.QueryString.Value;
}
@Html.PagedListPager(Model, page => Regex.IsMatch(query, @"[?&]page=\d+")
    ? Regex.Replace(query, @"([?&])page=\d+", $"$1page={page}")
    : (query.StartsWith("?") ? $"{query}&page={page}" : $"{query}?page={page}"),
    new X.PagedList.Web.Common.PagedListRenderOptionsBase
    {
        DisplayPageCountAndCurrentLocation = true,
        MaximumPageNumbersToDisplay = 5,
    })

这里从查询字符串中判断并替换 page 值,如果有更简单的方法敬请告知。比如 Webdiyer 的分页组件会自动携带所有参数。


更多使用方式参官方文档:https://github.com/dncuug/X.PagedList


附应用于 Unify Template(一款基于 Bootstrap 的 HTML 模板)中的配置:

<style>
    .u-pagination-v1-1--active .u-pagination-v1-1 { color: #fff; border-color: #72c02c; }
    .PagedList-pageCountAndLocation { float: right !important; }
</style>
@{
    string query = Context.Request.QueryString.Value;
}
@Html.PagedListPager(Model, page => Regex.IsMatch(query, @"[?&]page=\d+")
    ? Regex.Replace(query, @"([?&])page=\d+", $"$1page={page}")
    : (query.StartsWith("?") ? $"{query}&page={page}" : $"{query}?page={page}"),
    new X.PagedList.Web.Common.PagedListRenderOptionsBase
    {
        DisplayPageCountAndCurrentLocation = true,
        MaximumPageNumbersToDisplay = 5,
        UlElementClasses = new string[] { "list-inline" },
        LiElementClasses = new string[] { "list-inline-item" },
        PageClasses = new string[] { "u-pagination-v1__item", "u-pagination-v1-1", "g-pa-7-14" },
        ActiveLiElementClass = "u-pagination-v1-1--active",
        EllipsesElementClass = "g-pa-7-14",
    })


xoyozo 6 年前
10,219

ASP.NET Core 缓存 Caching 提供了包括但不限于以下几种存储方式:

内存缓存:https://xoyozo.net/Blog/Details/aspnetcore-memory-cache

SQL Server 缓存:https://xoyozo.net/Blog/Details/aspnetcore-sql-cache

Redis 缓存:https://xoyozo.net/Blog/Details/aspnetcore-redis-cache

MySQL 缓存:https://xoyozo.net/Blog/Details/aspnetcore-mysql-cache


本文介绍内存缓存

Memory Caching

1.新建一个 ASP.NET Core 项目,选择 Web 应用程序,将身份验证 改为 不进行身份验证。

2.添加引用

Install-Package Microsoft.Extensions.Caching.Memory

3.使用

在 Startup.cs 中 ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    // Add framework services.
    services.AddMvc();
}

然后在

public class HomeController : Controller
{
    private IMemoryCache _memoryCache;
    public HomeController(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    public IActionResult Index()
    {
        string cacheKey = "key";
        string result;
        if (!_memoryCache.TryGetValue(cacheKey, out result))
        {
            result = $"LineZero{DateTime.Now}";
            _memoryCache.Set(cacheKey, result);
        }
        ViewBag.Cache = result;
        return View();
    }
}

这里是简单使用,直接设置缓存。

我们还可以加上过期时间,以及移除缓存,还可以在移除时回掉方法。

过期时间支持相对和绝对。

下面是详细的各种用法。

public IActionResult Index()
{
    string cacheKey = "key";
    string result;
    if (!_memoryCache.TryGetValue(cacheKey, out result))
    {
        result = $"LineZero{DateTime.Now}";
        _memoryCache.Set(cacheKey, result);
        //设置相对过期时间2分钟
        _memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromMinutes(2)));
        //设置绝对过期时间2分钟
        _memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
            .SetAbsoluteExpiration(TimeSpan.FromMinutes(2)));
        //移除缓存
        _memoryCache.Remove(cacheKey);
        //缓存优先级 (程序压力大时,会根据优先级自动回收)
        _memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
            .SetPriority(CacheItemPriority.NeverRemove));
        //缓存回调 10秒过期会回调
        _memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
            .SetAbsoluteExpiration(TimeSpan.FromSeconds(10))
            .RegisterPostEvictionCallback((key, value, reason, substate) =>
            {
                Console.WriteLine($"键{key}值{value}改变,因为{reason}");
            }));
        //缓存回调 根据Token过期
        var cts = new CancellationTokenSource();
        _memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
            .AddExpirationToken(new CancellationChangeToken(cts.Token))
            .RegisterPostEvictionCallback((key, value, reason, substate) =>
            {
                Console.WriteLine($"键{key}值{value}改变,因为{reason}");
            }));
        cts.Cancel();
    }
    ViewBag.Cache = result;
    return View();
}


Distributed Cache Tag Helper

在 ASP.NET Core MVC 中有一个 Distributed Cache 我们可以使用。

我们直接在页面上增加 distributed-cache 标签即可。

<distributed-cache name="mycache" expires-after="TimeSpan.FromSeconds(10)">
    <p>缓存项10秒过期-LineZero</p>
    @DateTime.Now
</distributed-cache>
<distributed-cache name="mycachenew" expires-sliding="TimeSpan.FromSeconds(10)">
    <p>缓存项有人访问就不会过期,无人访问10秒过期-LineZero</p>
    @DateTime.Now
</distributed-cache>

这样就能缓存标签内的内容。


L
转自 LineZero 6 年前
8,416