本文使用 ASP.NET 6 版本 Senparc.Weixin.Sample.MP 示例项目改造。
第一步 注册多公众号
方法一:打开 appsettings.json 文件,在 SenparcWeixinSetting 节点内添加数组节点 Items,该对象类型同 SenparcWeixinSetting。
//Senparc.Weixin SDK 设置
"SenparcWeixinSetting": {
"IsDebug": true,
"Token": "",
"EncodingAESKey": "",
"WeixinAppId": "",
"WeixinAppSecret": "",
"Items": [
{
"IsDebug": true,
"Token": "a",
"EncodingAESKey": "a",
"WeixinAppId": "a",
"WeixinAppSecret": "a"
},
{
"IsDebug": true,
"Token": "b",
"EncodingAESKey": "b",
"WeixinAppId": "b",
"WeixinAppSecret": "b"
}
]
}
方法二:修改 Program.cs 文件,在 UseSenparcWeixin 方法中注册多个公众号信息。
var registerService = app.UseSenparcWeixin(app.Environment,
null /* 不为 null 则覆盖 appsettings 中的 SenparcSetting 配置*/,
null /* 不为 null 则覆盖 appsettings 中的 SenparcWeixinSetting 配置*/,
register => { /* CO2NET 全局配置 */ },
(register, weixinSetting) =>
{
//注册公众号信息(可以执行多次,注册多个公众号)
//register.RegisterMpAccount(weixinSetting, "【盛派网络小助手】公众号");
foreach (var mp in 从数据库或配置文件中获取的公众号列表)
{
register.RegisterMpAccount(new SenparcWeixinSetting
{
//IsDebug = true,
WeixinAppId = mp.AppId,
WeixinAppSecret = mp.AppSecret,
Token = mp.Token,
EncodingAESKey = mp.EncodingAeskey,
}, mp.Name);
}
});
完成后,我们可以从 Config.SenparcWeixinSetting.Items 获取这些信息。
第二步 接入验证与消息处理
打开 WeixinController 控制器,将构造函数改写为:
public WeixinController(IHttpContextAccessor httpContextAccessor)
{
AppId = httpContextAccessor.HttpContext!.Request.Query["appId"];
var MpSetting = Services.MPService.MpSettingByAppId(AppId);
Token = MpSetting.Token;
EncodingAESKey = MpSetting.EncodingAESKey;
}
示例中 Services.MPService.MpSettingByAppId() 方法实现从 Config.SenparcWeixinSetting.Items 返回指定 appId 的公众号信息。
为使 IHttpContextAccessor 注入生效,打开 Program.cs,在行
builder.Services.AddControllersWithViews();
下方插入
builder.Services.AddHttpContextAccessor();
这样,我们就可以在构造函数中直接获取地址栏中的 appId 参数,找到对应的公众号进行消息处理。
第三步 消息自动回复中的 AppId
打开 CustomMessageHandler.cs,将
private string appId = Config.SenparcWeixinSetting.MpSetting.WeixinAppId;
private string appSecret = Config.SenparcWeixinSetting.MpSetting.WeixinAppSecret;
替换为:
private string appId = null!;
private string appSecret = null!;
并在构造函数中插入
appId = postModel.AppId;
appSecret = Services.MPService.MpSettingByAppId(postModel.AppId).WeixinAppSecret;
这样,本页中使用的 appId / appSecret 会从 postModel 中获取,而非默认公众号。postModel 已在 WeixinController 中赋值当前的 appId。
改造后,我们可以在 OnTextRequestAsync() 等处理消息的方法中可以判断 appId 来处理不同的消息。
第四步 其它功能和接口
其它功能和接口均可用指定的 AppId 和对应的 AppSecret 进行调用。
您需要将 IWebHostEnvironment 注入到类中才能访问属性值 ApplicationBasePath:阅读有关依赖关系注入的信息。成功注入依赖项后,wwwroot 路径应该可供您使用。例如:
private readonly IWebHostEnvironment _appEnvironment;
public ProductsController(IWebHostEnvironment appEnvironment)
{
_appEnvironment = appEnvironment;
}
用法:
[HttpGet]
public IEnumerable<string> Get()
{
FolderScanner scanner = new FolderScanner(_appEnvironment.WebRootPath);
return scanner.scan();
}
编辑:在 ASP.NET 的更高版本中,IHostingEnvironment 已被替换为 IWebHostEnvironment。
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"
}]
}]
System.NotSupportedException: Serialization and deserialization of 'System.DateOnly' instances are not supported.
The unsupported member type is located on type 'System.Nullable`1[System.DateOnly]'.
解决方法:
https://www.nuget.org/packages/DateOnlyTimeOnly.AspNet/
builder.Services
.AddControllers(options => options.UseDateOnlyTimeOnlyStringConverters())
.AddJsonOptions(options => options.UseDateOnlyTimeOnlyStringConverters());
Nuget 包:System.Runtime.Caching
依赖注入:
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
定义键:
public static class CacheKeys
{
public static string Entry { get { return "_Entry"; } }
}
赋值与取值:
public IActionResult CacheTryGetValueSet()
{
DateTime cacheEntry;
// 尝试从缓存获取,若获取失败则重新赋值
if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
{
// 新的内容
cacheEntry = DateTime.Now;
// 缓存选项
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for this time, reset time if accessed.
.SetAbsoluteExpiration(TimeSpan.FromSeconds(3));
// 保存到缓存中
_cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
}
return View("Cache", cacheEntry);
}
注意:
.SetAbsoluteExpiration() 用于设置绝对过期时间,它表示只要时间一到就过期
.SetSlidingExpiration() 用于设置可调过期时间,它表示当离最后访问超过某个时间段后就过期
通过 nuget 安装 UEditorNetCore
从 UEditor 官网 下载最新的包 ueditorx_x_x_x-utf8-net.zip
解压包,并复制到项目的 wwwroot/lib 目录下,删除 net 目录。
根据 UEditorNetCore 官方的使用说明进行操作
步骤中,控制器 UEditorController 接替原 controller.ashx 的功能,用于统一处理上传逻辑
原 config.json 复制到项目根目录,用于配置上传相关设置(若更改文件名和路径在 services.AddUEditorService() 中处理)。
个人喜欢将 xxxPathFormat 值改为:upload/ueditor/{yyyy}{mm}{dd}/{time}{rand:6},方便日后迁移附件后进行批量替换。
记得配置 catcherLocalDomain 项。
上传相关的身份验证和授权控制请在 UEditorController 中处理,如:
public void Do()
{
if (用户未登录) { throw new System.Exception("请登录后再试"); }
_ue.DoAction(HttpContext);
}
如果图片仅仅上传到网站目录下,那么这样配置就结束了,如果要上传到阿里云 OSS 等第三方图床,那么继续往下看。
因 UEditorNetCore 虽然实现了上传到 FTP 和 OSS 的功能,但未提供配置相关的账号信息的途径,所以只能(1)重写 action,或(2)下载 github 的源代码,将核心项目加入到您的项目中进行修改。
重写 action 不方便后期维护,这里记录修改源码的方式。
将源码改造为可配置账号的上传到 OSS 的功能,具体步骤如下:
将 Consts.cs 从项目中排除。
打开 Handlers/UploadHandler.cs,找到 UploadConfig 类,将
FtpUpload 改为 OssUpload,
FtpAccount 改为 OssAccessKeyId,
FtpPwd 改为 OssAccessKeySecret,
FtpIp 改为 OssAccessEndpoint,
再添加一个属性 OssBucketName。
打开 Handlers/UploadHandler.cs,找到 Process() 方法,
将 UploadConfig.FtpUpload 改为 UploadConfig.OssUpload,
将 Consts.AliyunOssServer.* 改为 UploadConfig.Oss*。
打开 Handlers/CrawlerHandler.cs,找到 Fetch() 方法,
将 Config.GetValue<bool>("catcherFtpUpload") 改为 Config.GetValue<bool>("OssUpload"),
将 Consts.AliyunOssServer.* 改为 Config.GetString("Oss*")。
打开 UEditorActionCollection.cs,找到 UploadImageAction,将
FtpUpload = Config.GetValue<bool>("imageFtpUpload"), FtpAccount = Consts.ImgFtpServer.account, FtpPwd = Consts.ImgFtpServer.pwd, FtpIp = Consts.ImgFtpServer.ip,
替换为
OssUpload = Config.GetValue<bool>("OssUpload"), OssAccessKeyId = Config.GetString("OssAccessKeyId"), OssAccessKeySecret = Config.GetString("OssAccessKeySecret"), OssAccessEndpoint = Config.GetString("OssAccessEndpoint"), OssBucketName = Config.GetString("OssBucketName"),
其余 3 个 Action(UploadScrawlAction、UploadVideoAction、UploadFileAction)按同样的方式修改。
在所有创建 UploadHandler 对象时补充添加 SaveAbsolutePath 属性。
打开 config.json,添加相关配置项(注:配置文件中的 *FtpUpload 全部废弃,统一由 OssUpload 决定)
"OssUpload": true, "OssAccessEndpoint": "", "OssAccessKeyId": "", "OssAccessKeySecret": "", "OssBucketName": "",
将 xxxUrlPrefix 的值改为 OSS 对应的 CDN 网址(以 / 结尾,如://cdn.xoyozo.net/)。
其它注意点:
若使用 UEditorNetCore github 提供的源代码类库代替 nuget,且使用本地存储,那么需要将 Handlers 目录中与定义 savePath 相关的代码(查找 var savePath)替换成被注释的行
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
返回状态 | ASP.NET MVC | ASP.NET Core | ASP.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 JSON | return Json(object); | return Json(T); // 强制 JSON return Ok(T); // 请求时可指定 Content-Type | return Json(T); // 强制 JSON return Ok(T); // 请求时可指定 Content-Type |
200 JaveScript | return 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();
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
Nuget 安装:Microsoft.Extensions.Caching.SqlServer
执行 .NET Core CLI 命令,在数据库中创建表“TestCache”
dotnet sql-cache create "数据库连接字符串" dbo TestCache
若提示
找不到 "dotnet sql-cache" 命令,请运行以下命令进行安装
则运行
dotnet tool install --global dotnet-sql-cache
表和索引创建成功提示:
Table and index were created successfully.
表结构:
在 Startup.cs 文件的方法 ConfigureServices() 中添加:
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = "数据库连接字符串";
options.SchemaName = "dbo";
options.TableName = "TestCache";
});
在控制器中注入 IDistributedCache:
public class TestController : Controller
{
private readonly IDistributedCache cache;
public TestController(IDistributedCache cache)
{
this.cache = cache;
}
public IActionResult Index()
{
string k = "count";
int v = Convert.ToInt32(cache.GetString(k));
v++;
cache.SetString(k, v.ToString());
return Content(v.ToString());
}
}
结果:
更多用法详见官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed?view=aspnetcore-3.0
若要实现将 Session 保存至 SQL Server 中,参此文:https://xoyozo.net/Blog/Details/aspnetcore-session-sql
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
Nuget 安装:Microsoft.Extensions.Caching.StackExchangeRedis
在 Startup.cs 文件的方法 ConfigureServices() 中添加:
services.AddStackExchangeRedisCache(options =>
{
// 连接字符串
options.Configuration = "192.168.111.134,password=aW1HAyupRKmiZn3Q";
// 键名前缀
options.InstanceName = "xoyozo_";
});
在控制器中注入 IDistributedCache:
public class TestController : Controller
{
private readonly IDistributedCache cache;
public TestController(IDistributedCache cache)
{
this.cache = cache;
}
public IActionResult Index()
{
string k = "count";
int v = Convert.ToInt32(cache.GetString(k));
v++;
cache.SetString(k, v.ToString());
return Content(v.ToString());
}
}
结果:
更多用法详见官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed?view=aspnetcore-3.0
注:.NET 6 已不支持 Redis on Windows 3.0。
经过以上配置,Session 也会保存到 Redis 中,键名是 前缀_Guid 格式。
关于 Session,参此文:https://xoyozo.net/Blog/Details/aspnetcore-session-redis