.Net Core中AuthorizationHandlerContext如何获取当前请求的相关信息_娃都会打酱油了的博客-CSDN博客

mikel阅读(679)

来源: .Net Core中AuthorizationHandlerContext如何获取当前请求的相关信息_娃都会打酱油了的博客-CSDN博客

在.Net Core中要自定义用户身份认证,需要实现IAuthorizationHandler,实现的代码也比较简单,一般我们只要实现本地认证AuthorizationHandler<T>.HandleRequirementAsync即可,认证时一般需要用到一些用于判断是否允许访问的认证信息,比如当前的用户信息,比如当前请求的资源信息,这些信息呢,我们都可以通过AuthorizationHandlerContext来获取。

AuthorizationHandlerContext.Resource对应当前请求的资源信息,其返回值为object,所以我们也不知道这个值究竟是什么东西,但没关系,我们可以通过调试阶段的快速监视来查看实际Resource究竟是什么。

可以看到其类型为Microsoft.AspNetCore.Routing.RouteEndpoint,Endpoint.DisplayName为请求的action,继续展开,可以看到Endpoint.Metadata内包含了action对应的Attribute之类的信息,这些信息就是我们真正需要的内容。
PS:注意具体AuthorizationHandlerContext.Resource具体是什么和当前应用的宿主模式有关,我们开发时用的是默认的Kestrel模式,其它模式很可能不是Microsoft.AspNetCore.Routing.RouteEndpoint

上面说完了当前请求的资源部分,下面说当前用户信息部分,这部分就简单了,AuthorizationHandlerContext.User直接就可以获取当前的用户信息,如果这部分信息还不够,那你也可以在AuthorizationHandler<T>中直接注入你需要的内容,比如注入IHttpContextAccessor来获取当前请求的上下文,比如注入IOptions<T>来获取一些配置信息等。

下面举一个使用例子,该例子是设置接口在HttpGet时对所有人员公开,但非HttpGet请求时只有指定用户可以调用

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class UserLimitOperationAttribute : AuthorizeAttribute
{
/// <summary>
/// 直接指定当前的PolicyName
/// </summary>
public const string PolicyName = “UserLimitOperation”;
public UserLimitOperationAttribute()
: base(PolicyName)
{
}
}

public class UserLimitOperationRequirement : IAuthorizationRequirement
{
/// <summary>
/// 如果设置了<see cref=”UserLimitOperationAttribute”/>,但又没配置允许的用户,那么就认为对所有人开放(测试环境开放)
/// </summary>
public bool NoLimit
{
get
{
return this.Users.Count == 0;
}
}
public HashSet<string> Users { get; } = new HashSet<string>();
public UserLimitOperationRequirement(string limitUser)
{
if (!string.IsNullOrWhiteSpace(limitUser))
{
this.Users = limitUser.Split(new char[] { ‘,’ }, StringSplitOptions.RemoveEmptyEntries).ToHashSet();
}
}
}
public class UserLimitOperationHandler : AuthorizationHandler<UserLimitOperationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserLimitOperationRequirement requirement)
{
if (requirement.NoLimit)
{
context.Succeed(requirement);
}
else
{
var endpoint = context.Resource as RouteEndpoint;
if (endpoint != null)
{
var http = endpoint.Metadata.Where(_=>_ is HttpMethodAttribute).First() as HttpMethodAttribute;
if (http is HttpGetAttribute)
{//Get请求是公开的
context.Succeed(requirement);
return Task.CompletedTask;
}
}
if (context.User.Identity.IsAuthenticated
&& !string.IsNullOrWhiteSpace(context.User.Identity.Name) && requirement.Users.Contains(context.User.Identity.Name!))
{
//只有配置用户才允许调用
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
Startup.ConfigureServices注册代码如下

services.AddSingleton<IAuthorizationHandler, UserLimitOperationHandler>();
services.AddMvcCore().AddAuthorization(
options =>
{
options.AddPolicy(
UserLimitOperationAttribute.PolicyName,
policy =>
{
//这里的例子是通过IAuthorizationRequirement传递配置,也可以直接在AuthorizationHandler<T>中直接注入IOptions<T>
policy.AddRequirements(new UserLimitOperationRequirement(this.Configuration.GetValue<string>(“ConfigLimitUsers”)));
});
});
1
2
3
4
5
6
7
8
9
10
11
12
然后在要限制的controller或action上添加[UserLimitOperation]声明即可。

当然如果你的某些配置是直接在AuthorizeAttribute上指定的,比如指定资源标志的ResourceAuthorizeAttribute,那在判断时,你可以通过下面的代码来读取到相应的ResourceAuthorizeAttribute集合,然后进行逻辑判断即可。

var attributes = endpoint.Metadata.Where(_ => _ is ResourceAuthorizeAttribute).Cast<ResourceAuthorizeAttribute>().ToList();
1
最后再加个Log部分,个人老是忘记怎么自行指定日志的categoryName

ILoggerFactory loggerFactory;//注入
var loggerFactory.CreateLogger(“limitLog”);
1
2
可以扩展阅读的参考资料:

ASP.NET Core 认证与授权
浅析 .NET 中 AsyncLocal 的实现原理
————————————————
版权声明:本文为CSDN博主「娃都会打酱油了」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/starfd/article/details/119187464

ASP.NET Core的路由[1]:注册URL模式与HttpHandler的映射关系 - Artech - 博客园

mikel阅读(509)

来源: ASP.NET Core的路由[1]:注册URL模式与HttpHandler的映射关系 – Artech – 博客园

ASP.NET Core的路由是通过一个类型为RouterMiddleware的中间件来实现的。如果我们将最终处理HTTP请求的组件称为HttpHandler,那么RouterMiddleware中间件的意义在于实现请求路径与对应HttpHandler之间的映射关系。对于传递给RouterMiddleware中间件的每一个请求,它会通过分析请求URL的模式并选择并提取对应的HttpHandler来处理该请求。除此之外,请求的URL还会携带相应参数,该中间件在进行路由解析过程中还会根据生成相应的路由参数提供给处理该请求的Handler。为了让读者朋友们对实现在RouterMiddleware的路由功能具有一个大体的认识,我们照例先来演示几个简单的实例。[本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、注册请求路径与HttpHandler之间的映射
二、设置内联约束
三、为路由参数设置默认值
四、特殊的路由参数

一、注册请求路径与HttpHandler之间的映射

ASP.NET Core针对请求的处理总是在一个通过HttpContext对象表示的上下文中进行,所以上面我们所说的HttpHandler从编程的角度来讲体现为一个RequestDelegate的委托对象,因此所谓的“路由注册”就是注册一组具有相同默认的请求路径与对应RequestDelegate之间的映射关系。接下来我们就同一个简单的实例来演示这样的映射关系是如何通过注册RouterMiddleware中间件的方式来完成的。

我们演示的这个ASP.NET Core应用是一个简易版的天气预报站点。如果用户希望获取某个城市在未来N天之内的天气信息,他可以直接利用浏览器发送一个GET请求并将对应城市(采用电话区号表示)和天数设置在URL中。如下图所示,为了得到成都未来两天的天气信息,我们发送请求采用的路径为“weather/028/2”。对于路径“weather/0512/4”的请求,返回的自然就是苏州未来4天的添加信息。

1

为了实现这个简单的应用,我们定义如下一个名为WeatherReport的类型表示某个城市在某段时间范围类的天气。如下面的代码片段所示,我们定义了另一个名为WeatherInfo的类型来表示具体某一天的天气。简单起见,我们让这个WeatherInfo对象只携带基本添加状况和气温区间的信息。当我们创建一个WeatherReport对象的时候,我们会随机生成这些天气信息。

   1: public class WeatherReport
   2: {
   3:     private static string[]     _conditions = new string[] { "晴", "多云", "小雨" };
   4:     private static Random       _random = new Random();
   5:
   6:     public string                                 City { get; }
   7:     public IDictionary<DateTime, WeatherInfo>     WeatherInfos { get; }
   8:
   9:     public WeatherReport(string city, int days)
  10:     {
  11:         this.City = city;
  12:         this.WeatherInfos = new Dictionary<DateTime, WeatherInfo>();
  13:         for (int i = 0; i < days; i++)
  14:         {
  15:             this.WeatherInfos[DateTime.Today.AddDays(i + 1)] = new WeatherInfo
  16:             {
  17:                 Condition         = _conditions[_random.Next(0, 2)],
  18:                 HighTemperature   = _random.Next(20, 30),
  19:                 LowTemperature    = _random.Next(10, 20)
  20:             };
  21:         }
  22:     }
  23:
  24:     public WeatherReport(string city, DateTime date)
  25:     {
  26:         this.City = city;
  27:         this.WeatherInfos = new Dictionary<DateTime, WeatherInfo>
  28:         {
  29:             [date] = new WeatherInfo
  30:             {
  31:                 Condition          = _conditions[_random.Next(0, 2)],
  32:                 HighTemperature    = _random.Next(20, 30),
  33:                 LowTemperature     = _random.Next(10, 20)
  34:             }
  35:         };
  36:     }
  37:
  38:     public class WeatherInfo
  39:     {
  40:         public string Condition { get; set; }
  41:         public double HighTemperature { get; set; }
  42:         public double LowTemperature { get; set; }
  43:     }
  44: }

我们说最终用于处理请求的HttpHandler最终体现为一个类型为RequestDelegate的委托对象,为此我们定义了如下一个与这个委托类型具有一致声明的方法WeatherForecast来处理针对天气的请求。如下面的代码片段所示,我们在这个方法中直接调用HttpContext的扩展方法GetRouteData得到RouterMiddleware中间件在路由解析过程中得到的路由参数。这个GetRouteData方法返回的是一个具有字典结构的对象,它的Key和Value分别代表路由参数的名称和值,我们通过预先定义的参数名(“city”和“days”)得到目标城市和预报天数。

   1: public class Program
   2: {
   3:     private static Dictionary<string, string> _cities = new Dictionary<string, string>
   4:     {
   5:         ["010"]  = "北京",
   6:         ["028"]  = "成都",
   7:         ["0512"] = "苏州"
   8:     };
   9:
  10:     public static async Task WeatherForecast(HttpContext context)
  11:     {
  12:         string city = (string)context.GetRouteData().Values["city"];
  13:         city = _cities[city];
  14:         int days = int.Parse(context.GetRouteData().Values["days"].ToString());
  15:         WeatherReport report = new WeatherReport(city, days);
  16:
  17:         context.Response.ContentType = "text/html";
  18:         await context.Response.WriteAsync("<html><head><title>Weather</title></head><body>");
  19:         await context.Response.WriteAsync($"<h3>{city}</h3>");
  20:         foreach (var it in report.WeatherInfos)
  21:         {
  22:             await context.Response.WriteAsync($"{it.Key.ToString("yyyy-MM-dd")}:");
  23:             await context.Response.WriteAsync($"{it.Value.Condition}({it.Value.LowTemperature}℃ ~ {it.Value.HighTemperature}℃)<br/><br/>");
  24:         }
  25:         await context.Response.WriteAsync("</body></html>");
  26:     }
  27:
  28: }

有了这两个核心参数之后,我们据此生成一个WeatherReport对象,并将它携带的天气信息以一个HTML文档的形式响应给客户端,图1所示就是这个HTML文档在浏览器上的呈现效果。由于目标城市最初以电话区号的形式体现,在呈现天气信息的过程中我们还会根据区号获取具体城市名称,简单起见,我们利用一个简单的字典来保存区号和城市之间的关系,并且只存储了三个城市而已。

接下来我们来完成所需的路由注册工作,实际上就是注册RouterMiddleware中间件。由于这各中间件定义在“Microsoft.AspNetCore.Routing”这个NuGet包中,所以我们需要添加对应的依赖。如下面的代码片段所示,针对RouterMiddleware中间件的注册实现在ApplicationBuilder的扩展方法UseRouter中。由于RouterMiddleware中间件在进行路由解析的过程中需要使用到一些服务,我们调用WebHostBuilder的ConfigureServices方法注册的就是这些服务。具体来说,这些与路由相关的服务是通过调用ServiceCollection的扩展方法AddRouting实现的。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .ConfigureServices(svcs => svcs.AddRouting())
   8:             .Configure(app => app.UseRouter(builder => builder.MapGet("weather/{city}/{days}", WeatherForecast)))
   9:             .Build()
  10:             .Run();
  11:     }
  12:
  13: }

RouterMiddleware中间件针对路由的解析依赖于一个名为Router的对象,对应的接口为IRouter。我们在程序中会先根据ApplicationBuilder对象创建一个RouteBuilder对象,并利用后者来创建这个Router。我们说路由注册从本质上体现为注册某种URL模式与一个RequestDelegate对象之间的映射,这个映射关系的建立是通过调用RouteBuilder的MapGet方法的调用。MapGet方法具有两个参数,第一个参数代表映射的URL模板,后者是处理请求的RequestDelegate对象。我们指定的URL模板为“weather/{city}/{days}”,其中携带两个路由参数({city}和{days}),我们知道它代表获取天气预报的目标城市和天数。由于针对天气请求的处理实现在我们定义的WeatherReport方法中,我们将指向这个方法的RequestDelegate对象作为第二个参数。

二、设置内联约束

在上面进行路由注册的实例中,我们在注册的URL模板中定义了两个参数({city}和{days})来分别代表获取天气预报的目标城市对应的区号和天数。区号应该具有一定的格式(以零开始的3-4位数字),而天数除了必须是一个整数之外,还应该具有一定的范围。由于我们在注册的时候并没有为这个两个路由参数的取值做任何的约束,所以请求URL携带的任何字符都是有效的。而处理请求的WeatherForecast方法也并没有对提取的数据做任何的验证,所以在执行过程中会直接抛出异常。如下图所示,由于请求URL(“/weather/0512/iv”)指定了天数不合法,所有客户端接收到一个状态为“500 Internal Server Error”的响应。

2

为了确保路由参数数值的有效性,我们在进行路由注册的时候可以采用内联(Inline)的方式直接将相应的约束规则定义在路由模板中。ASP.NET Core针对我们常用的验证规则定义了相应的约束表达式,我们可以根据需要为某个路由参数指定一个或者多个约束表达式。

如下面的代码片段所示,为了确保URL携带的是合法的区号,我们为路由参数{city}应用了一个针对正则表达式的约束(:regex(^0[1-9]{{2,3}}$))。由于路由模板在被解析的时候会将“{…}”这样的字符理解为路由参数,如果约束表达式需要使用“{}”字符(比如正则表达式“^0[1-9]{2,3}$)”),需要采用“{{}}”进行转义。至于另一个路由参数{days}则应用了两个约束,第一个是针对数据类型的约束(:int),它要求参数值必须是一个整数。另一个是针对区间的约束(:range(1,4)),意味着我们的应用最多只提供未来4天的天气。

   1: string template = @"weather/{city:regex(^0\d{{2,3}}$)}/{days:int:range(1,4)}";
   2: new WebHostBuilder()
   3:     .UseKestrel()
   4:     .ConfigureServices(svcs => svcs.AddRouting())
   5:     .Configure(app => app.UseRouter(builder=> builder.MapGet(template, WeatherForecast)))
   6:     .Build()
   7:     .Run();

如果我们在注册路由的时候应用了约束,那么当RouterMiddleware中间件在进行路由解析的时候除了要求请求路径必须与路由模板具有相同的模式,同时还要求携带的数据满足对应路由参数的约束条件。如果不能同时满足这两个条件,RouterMiddleware中间件将无法选择一个RequestDelegate对象来处理当前请求,在此情况下它将直接将请求递交给后续的中间件进行处理。对于我们演示的这个实例来说,如果我们提供一个不合法的区号(1014)和预报天数(5),客户端都将得到一个状态码为“404 Not Found”的响应。

3

三、为路由参数设置默认值

路由注册时提供的路由模板(比如“Weather/{city}/{days}”)可以包含静态的字符(比如“weather”),也可以包括动态的参数(比如{city}和{days}),我们将它们成为路由参数。并非每个路由参数都是必需的(要求路由参数的值必需存在请求路径中),有的路由参数是可以缺省的。还是以上面演示的实例来说,我们可以采用如下的方式在路由参数名后面添加一个问号(“?”),原本必需的路由参数变成了可以缺省的。可缺省的路由参数只能出现在路由模板尾部,这个应该不难理解。

   1: string template = "weather/{city?}/{days?}";
   2: new WebHostBuilder()
   3:     .UseKestrel()
   4:     .ConfigureServices(svcs => svcs.AddRouting())
   5:     .Configure(app => app.UseRouter(builder=> builder.MapGet(template, WeatherForecast)))
   6:     .Build()
   7:     .Run();

既然可以路由变量占据的部分路径是可以缺省的,那么意味即使请求的URL不具有对应的内容(比如“weather”和“weather/010”),在进行路由解析的时候同样该请求与路由规则相匹配,但是在最终的路由参数字典中将找不到它们。由于表示目标城市和预测天数的两个路由参数都是可缺省的,我们需要对处理请求的WeatherForecast方法做作相应的改动。下面的代码片段表明如果请求URL为显式提供对应参数的数据,它们的默认值分别为“010”(北京)和4(天),也就是说应用默认提供北京地区未来四天的天气。

   1: public static async Task WeatherForecast(HttpContext context)
   2: {
   3:     object rawCity;
   4:     object rawDays;
   5:     var values = context.GetRouteData().Values;
   6:     string city = values.TryGetValue("city", out rawCity) ? rawCity.ToString() : "010";
   7:     int days = values.TryGetValue("days", out rawDays) ? int.Parse(rawDays.ToString()) : 4;
   8:
   9:     city = _cities[city];
  10:     WeatherReport report = new WeatherReport(city, days);
  11:
  12: }

针对上述的改动,如果希望获取北京未来四天的天气状况,我们可以采用如下图所示的三种URL(“weather”和“weather/010”和“weather/010/4”),它们都是完全等效的。

4

上面我们的程序相当于是在进行请求处理的时候给予了可缺省路由参数一个默认值,实际上路由参数默认值得设置还具有一种更简单的方式,那就是按照如下所示的方式直接将默认值定义在路由模板中。如果采用这样的路由注册方式,我们针对WeatherForecast方法的改动就完全没有必要了。

   1: string template = "weather/{city=010}/{days=4}";
   2: new WebHostBuilder()
   3:     .UseKestrel()
   4:     .ConfigureServices(svcs => svcs.AddRouting())
   5:     .Configure(app =>app.UseRouter(builder=>builder.MapGet(template, WeatherForecast)))
   6:     .Build()
   7:     .Run();

四、特殊的路由参数

一个URL可以通过分隔符“/”划分为多个路径分段(Segment),路由模板中定义的路由参数一般来说会占据某个独立的分段(比如“weather/{city}/{days}”)。不过也有特例,我们即可以在一个单独的路径分段中定义多个路由参数,同样也可以让一个路由参数跨越对个连续的路径分段。

我们先来介绍在一个独立的路径分段中定义多个路由参数的情况。同样以我们演示的获取天气预报的URL为例,假设我们设计一种URL来获取某个城市某一天的天气信息,比如“/weather/010/2016.11.11”这样一个URL可以获取北京地区在2016年双11那天的天气,那么路由模板为“/weather/{city}/{year}.{month}.{day}”。

   1: string tempalte = "weather/{city}/{year}.{month}.{day}";
   2: new WebHostBuilder()
   3:     .UseKestrel()
   4:     .ConfigureServices(svcs => svcs.AddRouting())
   5:     .Configure(app => app.UseRouter(builder=>builder.MapGet(tempalte, WeatherForecast)))
   6:     .Build()
   7:     .Run();
   8:
   9: public static async Task WeatherForecast(HttpContext context)
  10: {
  11:     var values     = context.GetRouteData().Values;
  12:     string city    = values["city"].ToString();
  13:     city           = _cities[city];
  14:     int year       = int.Parse(values["year"].ToString());
  15:     int month      = int.Parse(values["month"].ToString());
  16:     int day        = int.Parse(values["day"].ToString());
  17:
  18:     WeatherReport report = new WeatherReport(city, new DateTime(year,month,day));
  19:
  20: }

由于URL采用了新的设计,所以我们按照如上的形式对相关的程序进行了相应的修改。现在我们采用匹配的URL(比如“/weather/010/2016.11.11”)就可以获取到某个城市指定日期的天气。

5

对于上面设计的这个URL来说,我们采用“.”作为日期分隔符,如果我们采用“/”作为日期分隔符(比如“2016/11/11”),这个路由默认应该如何定义呢?由于“/”同时也是URL得路径分隔符,如果表示日期的路由变量也采用相同的分隔符,意味着同一个路由参数跨越了多个路径分段,我们只能定义“通配符”路由参数的形式来达到这个目的。通配符路由参数采用“{*variable}”这样的形式,星号(“*”)表示路径“余下的部分”,所以这样的路由参数只能出现在模板的尾端。对我们的实例来说,路由模板可以定义成“/weather/{city}/{*date}”。

   1: new WebHostBuilder()
   2:     .UseKestrel()
   3:     .ConfigureServices(svcs => svcs.AddRouting())
   4:     .Configure(app => {
   5:         string tempalte = "weather/{city}/{*date}";
   6:         IRouter router  = new RouteBuilder(app).MapGet(tempalte, WeatherForecast).Build();
   7:         app.UseRouter(router);
   8:     })
   9:     .Build()
  10:     .Run();
  11:
  12: public static async Task WeatherForecast(HttpContext context)
  13: {
  14:     var values      = context.GetRouteData().Values;
  15:     string city     = values["city"].ToString();
  16:     city            = _cities[city];
  17:     DateTime date   = DateTime.ParseExact(values["date"].ToString(), "yyyy/MM/dd",
  18:     CultureInfo.InvariantCulture);
  19:     WeatherReport report = new WeatherReport(city, date);
  20:
  21: }

我们可以对程序做如上的修改来使用新的URL模板(“/weather/{city}/{*date}”)。这样为了得到如上图所示的北京在2016年11月11日的天气,请求的URL可以替换成“/weather/010/2016/11/11”。


ASP.NET Core的路由[1]:注册URL模式与HttpHandler的映射关系
ASP.NET Core的路由[2]:路由系统的核心对象——Router
ASP.NET Core的路由[3]:Router的创建者——RouteBuilder
ASP.NET Core的路由[4]:来认识一下实现路由的RouterMiddleware中间件
ASP.NET Core的路由[5]:内联路由约束的检验

Visual Studio为C#控制台应用添加外部引用_Alphathur的博客-CSDN博客

mikel阅读(583)

来源: Visual Studio为C#控制台应用添加外部引用_Alphathur的博客-CSDN博客

问题
起因是我的控制台应用需要使用外部依赖OleDbConnection和OleDbDataAdapter来做excel数据处理。尽管在文件声明了

using System.Data.OleDb;
1
但似乎没有作用,如图提示:

错误 CS1069 未能在命名空间“System.Data.OleDb”中找到类型名“OleDbConnection”。此类型已转发到程序集“System.Data.OleDb, Version=4.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”。请考虑添加对该程序集的引用。 ConsoleApp1 C:\Users\usheryoung\source\repos\ConsoleApp1\ConsoleApp1\Program.cs 42 活动

通过项目引用可以看出确实缺少OleDb这个依赖

 

解决方案
在visual studio 工具栏搜索:程序包管理器控制台,然后执行

Install-Package System.Data.OleDb
1

OleDb即可被正常安装的控制台应用中,程序不再报错。
————————————————
版权声明:本文为CSDN博主「Alphathur」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/mryang125/article/details/117077030

.NET Core Session的使用方法 - zock - 博客园

mikel阅读(642)

来源: .NET Core Session的使用方法 – zock – 博客园

刚使用.NET Core会不习惯,比如如何使用Session;不仅需要引用相应的类库,还需要在Startup.cs里进行注册。

1、在你的项目上基于NuGet添加:

install-package  Microsoft.AspNetCore.Session -ver 2.0

install-package Microsoft.AspNetCore.Http.Extensions -ver 2.0

2、在Startup.cs里进行注册

在Startup.cs文件中的ConfigureServices方法中添加:

services.AddSession();

在Startup.cs文件中的Configure方法中添加:

app.UseSession();

添加后代码如下:

复制代码
public void ConfigureServices(IServiceCollection services)
{
    services.AddSession();
    services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();
    app.UseSession();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
    });
}
复制代码

3、在MVC Controller里使用HttpContext.Session

从nuget安装Microsoft.AspNetCore.Mvc引用,直接使用自带的方法进行设置和获取session。不过自带的方法设置和获取的session值是byte[]类型的,可以从nuget安装并引用Microsoft.AspNetCore.Http并使用里面的扩展方法。

复制代码
public class HomeController : Controller
{
    public IActionResult Index()
    {
        HttpContext.Session.SetString("code", "123456");
        return View();
    }

    public IActionResult About()
    {
        ViewBag.Code = HttpContext.Session.GetString("code");
        return View();
    }
}
复制代码

4、如果不是在Controller里,你可以注入IHttpContextAccessor

复制代码
public class SessionTestClass
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private ISession _session => _httpContextAccessor.HttpContext.Session;

    public SomeOtherClass(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void Set()
    {
        _session.SetString("code", "123456");
    }

    public void Get()
    {
        string code = _session.GetString("code");
    }
}
复制代码

5、Isession的扩展 存储复杂对象

复制代码
public static class SessionExtensions
{
    public static void SetObjectAsJson(this ISession session, string key, object value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));
    }

    public static T GetObjectFromJson<T>(this ISession session, string key)
    {
        var value = session.GetString(key);

        return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
    }
}
复制代码

使用范例:

var myTestObject = new MyTestClass();
HttpContext.Session.SetObjectAsJson("SessionTest", myTestObject);
var myComplexObject = HttpContext.Session.GetObjectFromJson<MyClass>("SessionTest");

 

2019年破解RAR密码的三种最可行方法

mikel阅读(1081)

来源: 2019年破解RAR密码的三种最可行方法

RAR是当今最流行的文件格式之一。它的用途范围从数据压缩到错误恢复,其主要优点是能够使用密码保护您的文件。但不知何故,当你忘记密码的时候会想。这可能是因为密码被您或其他人篡改了,或者您忘记了密码,因为它过于复杂和混乱。要破解受密码保护的RAR文件,您必须重新获取密码然后解锁文件。关于如何破解RAR密码有一些技巧,所以让我们看一下最简单的操作。


 

方法1.使用CMD破解RAR密码

使用Windows设备的命令提示符组件破解RAR存档的密码会导致如何在没有软件的情况下破解RAR密码,但它不如其他方法有效。您只需要利用命令提示符,否则称为“使用记事本删除RAR密码”。要这样做,你需要……

1.打开记事本,然后键入一些命令并运行它。

2.双击bat文件并按键盘上的Win + R键打开命令提示符,键入cmd.exe(或只是“cmd”),然后点击“确定”或按Enter键。

使用CMD破解RAR密码

3.键入RAR文件名,然后点击Enter键以键入RAR存档的路径。

4.转到RAR存档的属性并记下其名称和路径。

文件属性

5.在CMD的相应空格中键入名称和路径。

6.在新记事本中键入命令并使用标题“rar-password.bat”保存; 然后,按Enter键。

7.最后,让bat文件中的命令运行; 这将有助于找到已定义的RAR存档的密码。


 

方式2.在线破解RAR密码

是的,有几个网站专注于如何在线破解RAR密码。但在这里,我们将检查一个重置任何压缩文件密码的特定站点。

1.打开浏览器并访问https://www.catpasswd.com

rar密码破解工具

2.单击“选择加密文件”按钮,确保选中下面的框。值得注意的是,最大文件大小为100 MB。

3.选择文件后,输入您的邮箱以方便在恢复成功时可以及时的通知到您。

4.该站点现在将文件传输到服务器后利用云端超级计算机恢复; 当文件解密完成后,会向您发送邮件通知请注意查收。

同时,如果您的文件包含任何重要的业务信息或私人数据也完全没有任何问题,因为服务器是全程无人工感觉服务器自动运行的,在恢复成功后您还可以选择销毁文件以确保万无一失,就算服务器被黑客攻击也能保证您文件的绝对安全。显然,这是比使用程序破解好的原因,因为Web工具会利用云端超级服务器自动运行破解程序,方便快捷的同时不需要您额外多做任何事情,您只需要冲杯咖啡去做其他事情。


 

方式3.使用cRARk破解RAR密码

cRARk是为数不多的最快的RAR密码恢复工具之一。更重要的是,它完全免费使用。除了作为RAR密码恢复功能之外,cRARk还可用于不完整的密码和单词列表。它可以用字符补充单词表,这只是冰山一角。

由于其有趣的PCL语言,它使用一种强大的机制来恢复丢失或遗忘的RAR档案密码,并检查wordlist文件中的密码。尽管如此,这种方法在此列表中是最难的,并且不能在此完整地涵盖。

要使用此应用程序破解RAR密码,您需要……

1.首先,访问http://www.crark.net下载Windows或Linux版本

2.对于Windows用户,根据您的框架有两个版本:OpenCL和CUDA。

3.由于应用程序是命令行工具,您需要打开CMD窗口(Windows)或终端(Linux)并运行一些命令。

4.运行命令后,该工具会在几秒到几分钟内找到您的密码,具体取决于密码的长度。但是,如果找不到您的密码,它会通知您。


 

总结

列出了三种不同的方法后,您现在可以恢复RAR密码并再次解锁文件。即使这些方法看似乏味,但这些是帮助破解RAR密码的最简单,最直接的方法。使用上述方法 – 无论您决定使用命令提示符组件,通过CMD,cRARk或catpasswd的在线方法 – 您都可以获得密码保护的RAR文件而不受限制。

但是,解锁或破解受密码保护的RAR档案不是孩子的游戏。因此,需要耐心等待。密码分割程序将依赖于密码中使用的单词的数量和组合。如果密码包含超过4个字的复杂字母(小写和大写字母),数字(0到9)和独特字符(如 – !?>#$ <@),该程序将享有更多有机会解密RAR密码

ASP.Net core 中Server.MapPath的替换方法_shanghaimoon的博客-CSDN博客

mikel阅读(821)

string webRootPath = _hostingEnvironment.WebRootPath;             string contentRootPath = _hostingEnvironment.ContentRootPath;

来源: ASP.Net core 中Server.MapPath的替换方法_shanghaimoon的博客-CSDN博客

最近忙着将原来的ASP.NET项目迁移到ASP.NET core平台,整体还比较顺利,但其中也碰到不少问题,其中比比较值得关注的一个问题是,在netcore平台中,System.Web程序集已经取消了,要获取HttpContext并不是太容易,好在通过依赖注入,还是可以得到的,具体方法不在本文的讨论范围,大家可以自行百度。但是在得到了netcore版本的HttpContext后,发现已经不再有Server.MapPath函数了,而这个函数在以前是会被经常引用到的。

通过百度研究,发现也是有替代方法的,依然是通过强大的依赖注入,代码如下:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;

namespace AspNetCorePathMapping
{
public class HomeController : Controller
{
private readonly IHostingEnvironment _hostingEnvironment;

public HomeController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}

public ActionResult Index()
{
string webRootPath = _hostingEnvironment.WebRootPath;
string contentRootPath = _hostingEnvironment.ContentRootPath;

return Content(webRootPath + “\n” + contentRootPath);
}
}
}
从上面可以看出,通过WebRootPath的使用,基本可以达到Server.MapPath同样的效果。但是这是在controller类中使用,如果是在普通类库中改怎么获取呢,或者有没有更简洁的方法呢?答案是肯定的,先上代码:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace HH.Util
{
public static class CoreHttpContext
{
private static Microsoft.AspNetCore.Hosting.IHostingEnvironment _hostEnviroment;
public static string WebPath => _hostEnviroment.WebRootPath;

public static string MapPath(string path)
{
return Path.Combine(_hostEnviroment.WebRootPath, path);
}

internal static void Configure(Microsoft.AspNetCore.Hosting.IHostingEnvironment hostEnviroment)
{
_hostEnviroment = hostEnviroment;
}
}
public static class StaticHostEnviromentExtensions
{
public static IApplicationBuilder UseStaticHostEnviroment(this IApplicationBuilder app)
{
var webHostEnvironment = app.ApplicationServices.GetRequiredService<Microsoft.AspNetCore.Hosting.IHostingEnvironment>();
CoreHttpContext.Configure(webHostEnvironment);
return app;
}
}
}
然后在Startup.cs的Configure方法中:

app.UseStaticHostEnviroment();
这样的话,只需要将原来的Server.Path替换为CoreHttpContext.MapPath就可以了,移植难度大大降低。
————————————————
版权声明:本文为CSDN博主「shanghaimoon」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shanghaimoon/article/details/114338839

在ASP.NET Core中怎么使用HttpContext.Current - YOYOFx - 博客园

mikel阅读(587)

来源: 在ASP.NET Core中怎么使用HttpContext.Current – YOYOFx – 博客园

一、前言

我们都知道,ASP.NET Core作为最新的框架,在MVC5和ASP.NET WebForm的基础上做了大量的重构。如果我们想使用以前版本中的HttpContext.Current的话,目前是不可用的,因为ASP.NET Core中是并没有这个API的。

当然我们也可以通过在Controller中访问HttpContext,但是某些情况下,这样使用起来还是不如HttpContext.Current方便。

二、IHttpContextAccessor

利用ASP.NET Core的依赖注入容器系统,通过请求获取IHttpContextAccessor接口,我们拥有模拟使用HttpContext.Current这样API的可能性。但是因为IHttpContextAccessor接口默认不是由依赖注入进行实例管理的。我们先要将它注册到ServiceCollection中:

复制代码
public void ConfigureServices(IServiceCollection services)
{
    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    // Other code...
}
复制代码

来模拟一个HttpContext.Current吧:

复制代码
 public static class HttpContext
 {
        public static IServiceProvider ServiceProvider;

        public static Microsoft.AspNetCore.Http.HttpContext Current
        {
            get
            {
                object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor));
                Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext;
                return context;
            }
        }

}
复制代码

其实说到HttpContext.Current就不得不提到多线程问题,在以前的ASP.NET版本中,如果遇到多线程环境很有可能HttpContext.Current为空的情况。说到这个问题以前就是有解决方案的,那就是CallContext;

CallContext 是类似于方法调用的线程本地存储区的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽。数据槽不在其他逻辑线程上的调用上下文之间共享。当 CallContext 沿执行代码路径往返传播并且由该路径中的各个对象检查时,可将对象添加到其中。

当使用ASP.NET的时候,虽然线城池里的线程是复用的,但是CallContext并不在一个线程的多次使用中共享。因为CallContext是针对逻辑线程的TLS,线程池中被复用的线程是操作系统中的内核对象而不是托管对象。就像数据库连接池中保存的是非托管资源而不是托管资源。因此,先后执行的两个托管线程可能在底层复用了一个物理线程(内核对象),但并不能共享同一组CallContext数据槽。就像先后new的两个SQLConnection对象可能在底层使用了同一个物理连接,但是托管对象的属性已经被重置。

与此对照的是ThreadStaticAttribute,标记上这个特性的静态字段是往物理线程的TLS中保存数据(根据MSDN的描述猜的。具体没试过),因此如果两个托管线程对象内部使用的是同一个物理线程,则这个字段会复用(在两个线程通过这一字段访问同一个数据槽)。

在.NET Core中,也有新的API选择,AsyncLocal<T>。

三、HttpContextAccessor

我们来看看ASP.NET Core中的IHttpContextAccessor接口实现吧:

 

复制代码
 public class HttpContextAccessor : IHttpContextAccessor
 {
#if NET451
        private static readonly string LogicalDataKey = "__HttpContext_Current__" + AppDomain.CurrentDomain.Id;

        public HttpContext HttpContext
        {
            get
            {
                var handle = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle;
                return handle?.Unwrap() as HttpContext;
            }
            set
            {
                CallContext.LogicalSetData(LogicalDataKey, new ObjectHandle(value));
            }
        }

#elif NETSTANDARD1_3
        private AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();
        public HttpContext HttpContext
        {
            get
            {
                return _httpContextCurrent.Value;
            }
            set
            {
                _httpContextCurrent.Value = value;
            }
        }
#endif
}
复制代码

 

最后我只能说在ASP.NET Core中是万物皆DI啊,其实Core中的实现早就为我们想好了这些功能,只是改变了使用方式。

 

GitHub:https://github.com/maxzhang1985/YOYOFx  如果觉还可以请Star下, 欢迎一起交流。

 

.NET Core 开源学习群: 214741894  

将 ASP.NET MVC 应用升级到 .NET 6 - .NET Core | Microsoft Docs

mikel阅读(756)

来源: 将 ASP.NET MVC 应用升级到 .NET 6 – .NET Core | Microsoft Docs

.NET 升级助手是一种命令行工具,可帮助将 .NET Framework ASP.NET MVC 应用升级到 .NET 6。 本文提供以下内容:

  • 演示如何针对 .NET Framework ASP.NET MVC 应用运行该工具
  • 故障排除提示

升级 .NET Framework ASP.NET MVC 应用

本部分演示如何针对新创建的面向 .NET Framework 4.6.1 的 ASP.NET MVC 应用运行 NET 升级助手。 若要详细了解如何安装此工具,请查看 .NET 升级助手概述

初始演示设置

如果你要针对你自己的 .NET Framework 应用运行 .NET 升级助手,可跳过此步骤。 如果你只想试用一下来看看它的工作原理,可在此步骤中了解如何设置示例 ASP.NET MVC 和 Web API (.NET Framework) 项目以供使用。

借助 Visual Studio,使用 .NET Framework 创建一个新的 ASP.NET Web 应用程序项目。

在 Visual Studio 中新建 ASP.NET Web 应用程序项目

将项目命名为 AspNetMvcTest。 将项目配置为使用 .NET Framework 4.6.1。

在 Visual Studio 中配置 ASP.NET 项目

在下一对话框中,选择“MVC”应用程序,然后选择“创建” 。

在 Visual Studio 中创建 ASP.NET MVC 项目

查看所创建的项目及其文件,尤其是它的项目文件。

运行升级助手

打开终端,导航到目标项目或解决方案所在的文件夹。 运行 upgrade-assistant 命令,传入你要针对的项目的名称(可从任意位置运行该命令,只要项目文件的路径有效就行)。

控制台

upgrade-assistant upgrade .\AspNetMvcTest.csproj

该工具将运行并显示它将执行的步骤列表。

.NET 升级助手初始屏幕

完成每个步骤后,该工具都会提供一组命令,用户可应用这些命令,也可跳过下一步骤、查看更多详细信息、配置日志记录或退出该过程。 如果该工具检测到某个步骤将不执行任何操作,它会自动跳过该步骤,转到下一步骤,直到到达有要执行的操作的步骤为止。 如果未进行其他任何选择,那么按 Enter 将执行下一步

在此示例中,每次都会选择“应用”步骤。 第一步是备份项目。

.NET 升级助手备份项目

该工具会提示输入自定义路径进行备份或使用默认路径,后者会将项目备份放在具有 .backup 扩展名的同一文件夹中。 此工具接下来做的是将项目文件转换为 SDK 样式。

.NET 升级助手将项目转换为 SDK 样式

更新项目格式后,下一步是更新项目的 TFM。

.NET 升级助手更新 TFM

接下来,该工具会更新项目的 NuGet 包。 多个包需要更新,且会添加一个新的分析器包。

.NET 升级助手更新 NuGet 包

更新包后,接下来是添加模板文件(如果有)。 该工具指示有 4 个必须添加的预期模板项,随后它会添加这些项。 以下是模板文件的列表:

  • Program.cs
  • Startup.cs
  • appsettings.json
  • appsettings.Development.json

ASP.NET Core 会使用这些文件来进行应用启动配置

.NET 升级助手添加模板文件

接下来,该工具会迁移配置文件。 该工具会标识应用设置并禁用不受支持的配置部分,然后迁移 appSettings 配置值。

.NET 升级助手迁移配置

该工具通过迁移 system.web.webPages.razor/pages/namespaces 来完成配置文件的迁移。

.NET 升级助手迁移配置已完成

该工具会应用已知的修补程序来将 C# 引用迁移到其新的对应项。

.NET 升级助手更新 C# 源

这是最后一个项目,因此下一步是“移动到新的项目”,它提示完成迁移整个解决方案的过程。

.NET 升级助手完成解决方案

完成此过程后,打开项目文件并进行查看。 查找静态文件,如下所示:

XML

  <ItemGroup>
    <Content Include="fonts\glyphicons-halflings-regular.woff2" />
    <Content Include="fonts\glyphicons-halflings-regular.woff" />
    <Content Include="fonts\glyphicons-halflings-regular.ttf" />
    <Content Include="fonts\glyphicons-halflings-regular.eot" />
    <Content Include="Content\bootstrap.min.css.map" />
    <Content Include="Content\bootstrap.css.map" />
    <Content Include="Content\bootstrap-theme.min.css.map" />
    <Content Include="Content\bootstrap-theme.css.map" />
    <Content Include="Scripts\jquery-3.4.1.slim.min.map" />
    <Content Include="Scripts\jquery-3.4.1.min.map" />
  </ItemGroup>

该由 Web 服务器处理的静态文件应移动到名为 wwwroot 的根级别文件夹下适当的文件夹中。 有关详细信息,请查看 ASP.NET Core 中的静态文件 。 移动文件后,可删除项目文件中与这些文件对应的 <Content> 元素。 事实上,可删除所有 <Content> 元素及其包含组。 此外,应删除指向客户端库(如 bootstrap 或 JQuery)的所有 <PackageReference>

默认情况下,项目将被转换为类库。 请将第一行的 Sdk 属性更改为 Microsoft.NET.Sdk.Web,并将 <TargetFramework> 设置为 net5.0。 编译该项目。 此时,错误数应当相当小。 在移植新的 ASP.NET 4.6.1 MVC 项目时,其余错误引用 App_Start 文件夹中的文件:

  • BundleConfig.cs
  • FilterConfig.cs
  • RouteConfig.cs

可删除这些文件和整个 App_Start 文件夹。 同样,可删除 Global.asax 和 Global.asax.cs 文件。

此时,只剩下与捆绑相关的错误。 可通过多种方式在 SP.NET Core 中配置捆绑和缩减。 选择最适合你的项目的任何内容。

故障排除提示

使用 .NET 升级助手时,可能会出现一些已知问题。 某些情况下,.NET 升级助手在内部使用的 try-convert 工具会出现问题。

有关更多故障排除提示和已知问题,可查看此工具的 GitHub 存储库

另请参阅

Python之flask框架_网络星空(luoc)的博客-CSDN博客_python框架flask

mikel阅读(804)

来源: Python之flask框架_网络星空(luoc)的博客-CSDN博客_python框架flask

文章目录
前言
安装flask
调试模式
路由
路径变量
构造URL
HTTP方法
静态文件
日志输出
处理请求
Request 对象
文件上传
Cookies
重定向和错误
响应处理
Sessions
模板简介
模板标签
继承
控制流
总结
前言
Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务。本文参考自Flask官方文档,大部分代码引用自官方文档。

安装flask
首先我们来安装Flask。最简单的办法就是使用pip。
pip install flask
1
然后打开一个Python文件,输入下面的内容并运行该文件。然后访问localhost:5000,我们应当可以看到浏览器上输出了hello world。
from flask import Flask
app = Flask(__name__)

@app.route(‘/’)
def hello_world():
return ‘hello world’

if __name__ == ‘__main__’:
app.run(host=’127.0.0.1′,port=5000)
1
2
3
4
5
6
7
8
9
调试模式
我们修改代码中的输出,然后查看浏览器上是否有变化。如果你照做的话,可以看到什么变化都没有。其实Flask内置了调试模式,可以自动重载代码并显示调试信息。这需要我们开启调试模式,方法很简单,设置FLASK_Debug环境变量,并将值设置为1。或者设置app.Debug=True

from flask import Flask

app = Flask(__name__)
app.debug=True

@app.route(‘/’)
def hello_world():
return ‘Hello World!’

@app.route(‘/login’)
def login():
return ‘Login’

if __name__ == ‘__main__’:
app.run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
然后再次运行程序,会看到有这样的输出。这时候如果再次修改代码,会发现这次Flask会自动重启。

* Restarting with stat
* Debugger is active!
* Debugger PIN: 157-063-180
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
1
2
3
4
路由
在上面的例子里可以看到路由的使用。如果了解Spring Web MVC的话,应该对路由很熟悉。路由通过使用Flask的app.route装饰器来设置,这类似Java的注解。

@app.route(‘/’)
def index():
return ‘Index Page’

@app.route(‘/hello’)
def hello():
return ‘Hello, World’
1
2
3
4
5
6
7
路径变量
如果希望获取/article/1这样的路径参数,就需要使用路径变量。路径变量的语法是/path/converter:varname。在路径变量前还可以使用可选的转换器,有以下几种转换器。

下面是Flask官方的例子。

@app.route(‘/user/<username>’)
def show_user_profile(username):
# show the user profile for that user
return ‘User %s’ % username

@app.route(‘/post/<int:post_id>’)
def show_post(post_id):
# show the post with the given id, the id is an integer
return ‘Post %d’ % post_id
1
2
3
4
5
6
7
8
9
构造URL
在Web程序中常常需要获取某个页面的URL,在Flask中需要使用url_for(‘方法名’)来构造对应方法的URL。下面是Flask官方的例子。

>>> from flask import Flask, url_for
>>> app = Flask(__name__)
>>> @app.route(‘/’)
… def index(): pass

>>> @app.route(‘/login’)
… def login(): pass

>>> @app.route(‘/user/<username>’)
… def profile(username): pass

>>> with app.test_request_context():
… print url_for(‘index’)
… print url_for(‘login’)
… print url_for(‘login’, next=’/’)
… print url_for(‘profile’, username=’John Doe’)

/
/login
/login?next=/
/user/John%20Doe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
HTTP方法
如果需要处理具体的HTTP方法,在Flask中也很容易,使用route装饰器的methods参数设置即可。

from flask import request

@app.route(‘/login’, methods=[‘GET’, ‘POST’])
def login():
if request.method == ‘POST’:
do_the_login()
else:
show_the_login_form()
1
2
3
4
5
6
7
8
静态文件
Web程序中常常需要处理静态文件,在Flask中需要使用url_for函数并指定static端点名和文件名。在下面的例子中,实际的文件应放在static/文件夹下。

url_for(‘static’, filename=’style.css’)
1
模板生成
Flask默认使用Jinja2作为模板,Flask会自动配置Jinja 模板,所以我们不需要其他配置了。默认情况下,模板文件需要放在templates文件夹下。

使用 Jinja 模板,只需要使用render_template函数并传入模板文件名和参数名即可。

from flask import render_template

@app.route(‘/hello/’)
@app.route(‘/hello/<name>’)
def hello(name=None):
return render_template(‘hello.html’, name=name)
1
2
3
4
5
6
相应的模板文件如下。

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
1
2
3
4
5
6
7
日志输出
Flask 为我们预配置了一个 Logger,我们可以直接在程序中使用。这个Logger是一个标准的Python Logger,所以我们可以向标准Logger那样配置它,详情可以参考官方文档或者我的文章Python 日志输出。

app.logger.debug(‘A value for debugging’)
app.logger.warning(‘A warning occurred (%d apples)’, 42)
app.logger.error(‘An error occurred’)
1
2
3
处理请求
在 Flask 中获取请求参数需要使用request等几个全局对象,但是这几个全局对象比较特殊,它们是 Context Locals ,其实就是 Web 上下文中局部变量的代理。虽然我们在程序中使用的是全局变量,但是对于每个请求作用域,它们都是互不相同的变量。理解了这一点,后面就非常简单了。

Request 对象
Request 对象是一个全局对象,利用它的属性和方法,我们可以方便的获取从页面传递过来的参数。

method属性会返回HTTP方法的类似,例如post和get。form属性是一个字典,如果数据是POST类型的表单,就可以从form属性中获取。下面是 Flask 官方的例子,演示了 Request 对象的method和form属性。

from flask import request

@app.route(‘/login’, methods=[‘POST’, ‘GET’])
def login():
error = None
if request.method == ‘POST’:
if valid_login(request.form[‘username’],
request.form[‘password’]):
return log_the_user_in(request.form[‘username’])
else:
error = ‘Invalid username/password’
# the code below is executed if the request method
# was GET or the credentials were invalid
return render_template(‘login.html’, error=error)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
如果数据是由GET方法传送过来的,可以使用args属性获取,这个属性也是一个字典。

searchword = request.args.get(‘key’, ”)
1
文件上传
利用Flask也可以方便的获取表单中上传的文件,只需要利用 request 的files属性即可,这也是一个字典,包含了被上传的文件。如果想获取上传的文件名,可以使用filename属性,不过需要注意这个属性可以被客户端更改,所以并不可靠。更好的办法是利用werkzeug提供的secure_filename方法来获取安全的文件名。

from flask import request
from werkzeug.utils import secure_filename

@app.route(‘/upload’, methods=[‘GET’, ‘POST’])
def upload_file():
if request.method == ‘POST’:
f = request.files[‘the_file’]
f.save(‘/var/www/uploads/’ + secure_filename(f.filename))
1
2
3
4
5
6
7
8
Cookies
Flask也可以方便的处理Cookie。使用方法很简单,直接看官方的例子就行了。下面的例子是如何获取cookie。

from flask import request

@app.route(‘/’)
def index():
username = request.cookies.get(‘username’)
# 使用 cookies.get(key) 代替 cookies[key] 避免
# 得到 KeyError 如果cookie不存在
1
2
3
4
5
6
7
重定向和错误
redirect和abort函数用于重定向和返回错误页面。

from flask import abort, redirect, url_for

@app.route(‘/’)
def index():
return redirect(url_for(‘login’))

@app.route(‘/login’)
def login():
abort(401)
this_is_never_executed()
1
2
3
4
5
6
7
8
9
10
默认的错误页面是一个空页面,如果需要自定义错误页面,可以使用errorhandler装饰器。

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
return render_template(‘page_not_found.html’), 404
1
2
3
4
5
响应处理
默认情况下,Flask会根据函数的返回值自动决定如何处理响应:如果返回值是响应对象,则直接传递给客户端;如果返回值是字符串,那么就会将字符串转换为合适的响应对象。我们也可以自己决定如何设置响应对象,方法也很简单,使用make_response函数即可。

@app.errorhandler(404)
def not_found(error):
resp = make_response(render_template(‘error.html’), 404)
resp.headers[‘X-Something’] = ‘A value’
return resp
1
2
3
4
5
Sessions
我们可以使用全局对象session来管理用户会话。Sesison 是建立在 Cookie 技术上的,不过在 Flask 中,我们还可以为 Session 指定密钥,这样存储在 Cookie 中的信息就会被加密,从而更加安全。直接看 Flask 官方的例子吧。

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)

@app.route(‘/’)
def index():
if ‘username’ in session:
return ‘Logged in as %s’ % escape(session[‘username’])
return ‘You are not logged in’

@app.route(‘/login’, methods=[‘GET’, ‘POST’])
def login():
if request.method == ‘POST’:
session[‘username’] = request.form[‘username’]
return redirect(url_for(‘index’))
return ”’
<form method=”post”>
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
”’

@app.route(‘/logout’)
def logout():
# remove the username from the session if it’s there
session.pop(‘username’, None)
return redirect(url_for(‘index’))

# set the secret key. keep this really secret:
app.secret_key = ‘A0Zr98j/3yX R~XHH!jmN]LWX/,?RT’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
模板简介
这里简单的介绍一下Jinja 模板的使用方法,详细资料直接看原文档吧。

模板标签
其实Jinja 模板和其他语言和框架的模板类似,反正都是通过某种语法将HTML文件中的特定元素替换为实际的值。如果使用过JSP、Thymeleaf 等模板,应该可以非常容易的学会使用 Jinja模板。

其实从上面的例子中我们应该可以看到Jinja 模板的基本语法了。代码块需要包含在{% %}块中,例如下面的代码。

{% extends ‘layout.html’ %}
{% block title %}主页{% endblock %}
{% block body %}

<div class=”jumbotron”>
<h1>主页</h1>
</div>

{% endblock %}
1
2
3
4
5
6
7
8
9
双大括号中的内容不会被转义,所有内容都会原样输出,它常常和其他辅助函数一起使用。下面是一个例子。

<a class=”navbar-brand” href={{ url_for(‘index’) }}>Flask小例子</a>
1
继承
模板可以继承其他模板,我们可以将布局设置为父模板,让其他模板继承,这样可以非常方便的控制整个程序的外观。

例如这里有一个layout.html模板,它是整个程序的布局文件。

<!DOCTYPE html>
<html>
<head>
<meta charset=”UTF-8″>
<meta http-equiv=”X-UA-Compatible” content=”IE=edge”>
<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<title>{% block title %}{% endblock %}</title>
<link rel=”stylesheet” href=”{{ url_for(‘static’,filename=’css/bootstrap.css’) }}”/>
<link rel=”stylesheet” href=”{{ url_for(‘static’,filename=’css/bootstrap-theme.css’) }}”/>

</head>
<body>

<div class=”container body-content”>
{% block body %}{% endblock %}
</div>

<div class=”container footer”>
<hr>
<p>这是页脚</p>
</div>

<script src=”{{ url_for(‘static’,filename=’js/JQuery.js’) }}”></script>
<script src=”{{ url_for(‘static’,filename=’js/bootstrap.js’) }}”></script>

</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
其他模板可以这么写。对比一下面向对象编程的继承概念,我们可以很容易的理解。

{% extends ‘layout.html’ %}
{% block title %}主页{% endblock %}
{% block body %}

<div class=”jumbotron”>
<h1>主页</h1>
<p>本项目演示了Flask的简单使用方法,点击导航栏上的菜单条查看具体功能。</p>
</div>

{% endblock %}
1
2
3
4
5
6
7
8
9
10
控制流
条件判断可以这么写,类似于JSP标签中的Java 代码,{% %}中也可以写Python代码。下面是Flask官方文档的例子。

<div class=metanav>
{% if not session.logged_in %}
<a href=”{{ url_for(‘login’) }}”>log in</a>
{% else %}
<a href=”{{ url_for(‘logout’) }}”>log out</a>
{% endif %}
</div>
1
2
3
4
5
6
7
循环的话可以这么写,和在Python中遍历差不多。

<tbody>
{% for key,value in data.items() %}
<tr>
<td>{{ key }}</td>
<td>{{ value }}</td>
</tr>
{% endfor %}
<tr>
<td>文件</td>
<td></td>
</tr>
</tbody>
1
2
3
4
5
6
7
8
9
10
11
12
需要注意不是所有的Python代码都可以写在模板里,如果希望从模板中引用其他文件的函数,需要显式将函数注册到模板中。可以参考这个爆栈提问。

总结
这篇文章主要参考了Flask的官方文档,但是只介绍了 Flask的最基本的一部分。了解了这部分,我们可以用Python 搭一个小服务器做点事情。如果希望详细了解 Flask的使用用法,请关注更详细的资料。本文就是起一个抛砖引玉的效果。

顺便说,通过Flask 我也了解了Python 语言的执行速度。我们都知道编译器编译出来的代码执行起来要比解释器解释代码要快大约几十倍到几千倍不等。以前学Java的时候,感觉Java 慢,主要原因就是等待编译时间比较长。相对来说用Python写脚本就很块了,因为没有编译过程。

但是从Flask的运行速度来看,我切身感受到了Python 执行确实不快。举个例子,在Spring中写一个控制器,接受HTTP参数,并显示到页面上,如果程序编译完之后,这个显示过程基本是瞬时的。但是同样的需求在Flask中,我居然可以感觉到明显的延迟(大概几百毫秒的等待时间)。所以,如果你想写一个比较快的Web程序,还是用Java或者JVM语言吧,虽然看着土,性能确实杠杠的 。
————————————————
版权声明:本文为CSDN博主「网络星空(luoc)」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42483745/article/details/123957659

pythonFlask框架学习 - 简书

mikel阅读(765)

来源: pythonFlask框架学习 – 简书

Flask是由python实现的一个web微框架,让我们可以使用Python语言快速实现一个网站或Web服务。而且有对应的python3及python2版本。首先这边选择的是python3.6,虽然python3在网上好像名声不咋地,而且一度有文章说python3正在毁灭Python,但是反正是别人选的,也就将就了。

在网上看别人下载个flask很麻烦,反正我的很简单,windows环境下的

1.安装Flask

pip install flask

2.目录结构

通过别人的目录大致了解一下flask框架的目录结构。

flask-demo/
  ├ run.py           # 应用启动程序
  ├ config.py        # 环境配置
  ├ requirements.txt # 列出应用程序依赖的所有Python包
  ├ tests/           # 测试代码包
  │   ├ __init__.py 
  │   └ test_*.py    # 测试用例
  └ myapp/
      ├ admin/       # 蓝图目录
      ├ static/
      │   ├ css/     # css文件目录
      │   ├ img/     # 图片文件目录
      │   └ js/      # js文件目录
      ├ templates/   # 模板文件目录
      ├ __init__.py    
      ├ forms.py     # 存放所有表单,如果多,将其变为一个包
      ├ models.py    # 存放所有数据模型,如果多,将其变为一个包
      └ views.py     # 存放所有视图函数,如果多,将其变为一个包

3.开始 Hello world

最简单的测试

from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
    return 'Hello World'
if __name__ == '__main__':
    app.debug = True # 设置调试模式,生产模式的时候要关掉debug
    app.run()

这是flask框架制作的一个最小的应用。使用python运行后访问localhost:5000就能看到网页显示Hello world。
这里首先引入了Flask类,然后给这个类创建了一个实例,name代表这个模块的名字。因为这个模块是直接被运行的所以此时name的值是main。然后用route()这个修饰器定义了一个路由,告诉flask如何访问该函数。最后用run()函数使这个应用在服务器上运行起来。

4.模板

Flask的模板功能是基于Jinja2模板引擎实现的。让我们来实现一个例子吧。创建一个新的Flask运行文件(你应该不会忘了怎么写吧),代码如下:

from  flask import  Flask

from  flask import  render_template

app  =  Flask(__name__)

@app.route('/hello')

@app.route('/hello/<name>')

def  hello(name=None):

    return  render_template('hello.html',  name=name)

if  __name__  ==  '__main__':

    app.run(host='0.0.0.0',  debug=True)

这段代码”hello()”函数并不是直接返回字符串,而是调用了”render_template()”方法来渲染模板。方法的第一个参数”hello.html”指向你想渲染的模板名称,第二个参数”name”是你要传到模板去的变量,变量可以传多个。

那么这个模板”hello.html”在哪儿呢,变量参数又该怎么用呢?接下来我们创建模板文件。在当前目录下,创建一个子目录”templates”(注意,一定要使用这个名字)。然后在”templates”目录下创建文件”hello.html”,内容如下:

<!doctype html>

<title>Hello Sample</title>

{% if name %}

  <h1>Hello {{ name }}!</h1>

{% else %}

  <h1>Hello World!</h1>

{%  endif  %}

这段代码是不是很像HTML?接触过其他模板引擎的朋友们肯定立马秒懂了这段代码。它就是一个HTML模板,根据”name”变量的值,显示不同的内容。变量或表达式由”{{ }}”修饰,而控制语句由”{% %}”修饰,其他的代码,就是我们常见的HTML。

让我们打开浏览器,输入”http://localhost:5000/hello/man”,页面上即显示大标题”Hello man!”。我们再看下页面源代码

<!doctype html>

<title>Hello from Flask</title>

  <h1>Hello man!</h1>

果然,模板代码进入了”Hello {{ name }}!”分支,而且变量”{{ name }}”被替换为了”man”。Jinja2的模板引擎还有更多强大的功能,包括for循环,过滤器等。模板里也可以直接访问内置对象如request, session等。对于Jinja2的细节,感兴趣的朋友们可以自己去查查。

5.模板继承

一般我们的网站虽然页面多,但是很多部分是重用的,比如页首,页脚,导航栏之类的。对于每个页面,都要写这些代码,很麻烦。Flask的Jinja2模板支持模板继承功能,省去了这些重复代码。让我们基于上面的例子,在”templates”目录下,创建一个名为”layout.html”的模板:

<!doctype html>

<title>Hello Sample</title>

<link rel="stylesheet"  type="text/css"  href="{{ url_for('static', filename='style.css') }}">

<div class="page">

    {% block body %}

    {% endblock %}

</div>

再修改之前的”hello.html”,把原来的代码定义在”block body”中,并在代码一开始”继承”上面的”layout.html”:

{%  extends  "layout.html"  %}

{%  block  body  %}

{%  if  name  %}

  <h1>Hello {{ name }}!</h1>

{% else %}

  <h1>Hello World!</h1>

{%  endif  %}

{%  endblock  %}

打开浏览器,再看下”http://localhost:5000/hello/man”页面的源码。

<!doctype html>

<title>Hello Sample</title>

<link rel="stylesheet"  type="text/css"  href="/static/style.css">

<div class="page">

  <h1>Hello man!</h1>

</div>

你会发现,虽然”render_template()”加载了”hello.html”模板,但是”layout.html”的内容也一起被加载了。而且”hello.html”中的内容被放置在”layout.html”中”{% block body %}”的位置上。形象的说,就是”hello.html”继承了”layout.html”。

6. HTML自动转义

我们看下下面的代码:

@app.route('/')

def  index():

    return  '<div>Hello %s</div>'  %  '<em>Flask</em>'

打开页面,你会看到”Hello Flask”字样,而且”Flask”是斜体的,因为我们加了”em”标签。但有时我们并不想让这些HTML标签自动转义,特别是传递表单参数时,很容易导致HTML注入的漏洞。我们把上面的代码改下,引入”Markup”类:

from  flask import  Flask,  Markup

app  =  Flask(__name__)

@app.route('/')

def  index():

    return  Markup('<div>Hello %s</div>')  %  '<em>Flask</em>'

再次打开页面,”em”标签显示在页面上了。Markup还有很多方法,比如”escape()”呈现HTML标签, “striptags()”去除HTML标签。这里就不一一列举了。

7.Request 对象

from flask import Flask,url_for,request,render_template

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        if request.form['user'] == 'admin':
            return 'Admin login successfully!'
        else:
            return 'No such user!'
    title = request.args.get('title', 'Default')
    return render_template('login.html', title=title)
if __name__ == "__main__":
    app.run(debug=True)

简单解释下,request中”method”变量可以获取当前请求的方法,即”GET”, “POST”, “DELETE”, “PUT”等;”form”变量是一个字典,可以获取Post请求表单中的内容,在上例中,如果提交的表单中不存在”user”项,则会返回一个”KeyError”,你可以不捕获,页面会返回400错误(想避免抛出这”KeyError”,你可以用request.form.get(“user”)来替代)。而”request.args.get()”方法则可以获取Get请求URL中的参数,该函数的第二个参数是默认值,当URL参数不存在时,则返回默认值。

在当前目录下,创建一个子目录”templates”(注意,一定要使用这个名字)。然后在”templates”目录下,添加
layout.html

<!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<div class="page">
    {% block body %}
    {% endblock %}
</div>

login.html

{% extends "layout.html" %}
{% block body %}
<form name="login" action="/login" method="post">
    Hello {{ title }}, please login by:
    <input type="text" name="user" />
</form>
{% endblock %}
图片.png
图片.png

8.会话session

会话可以用来保存当前请求的一些状态,以便于在请求之前共享信息。

from flask import Flask,url_for,request,render_template,redirect,session
@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        if request.form['user'] == 'admin':
            session['user'] = request.form['user']
            return 'Admin login successfully!'
        else:
            return 'No such user!'
    if 'user' in session:
        return 'Hello %s!' % session['user']
    else:
        title = request.args.get('title', 'Default')
        return render_template('login.html', title=title)
@app.route('/logout')
def logout():
    session.pop('user', None)
    return redirect(url_for('login'))

app.secret_key = '123456'
if __name__ == "__main__":
    app.run(debug=True)

你可以看到,”admin”登陆成功后,再打开”login”页面就不会出现表单了。然后打开logout页面可以登出。session对象的操作就跟一个字典一样。特别提醒,使用session时一定要设置一个密钥”app.secret_key”,如上例。不然你会得到一个运行时错误,内容大致是”RuntimeError: the session is unavailable because no secret key was set”。密钥要尽量复杂,最好使用一个随机数,这样不会有重复,上面的例子不是一个好密钥。

9.构建响应

在之前的例子中,请求的响应我们都是直接返回字符串内容,或者通过模板来构建响应内容然后返回。其实我们也可以先构建响应对象,设置一些参数(比如响应头)后,再将其返回。修改下上例中的Get请求部分:

from flask import Flask,url_for,request,render_template,redirect,session,make_response

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        ...
    if 'user' in session:
        ...
    else:
        title = request.args.get('title', 'Default')
        response = make_response(render_template('login.html', title=title), 200)
        response.headers['key'] = 'value'
        return response
if __name__ == "__main__":
    app.run(debug=True)

打开浏览器调试,在Get请求用户未登录状态下,你会看到响应头中有一个”key”项。”make_response”方法就是用来构建response对象的,第二个参数代表响应状态码,缺省就是200。

图片.png

10.Cookie的使用

提到了Session,当然也要介绍Cookie喽,毕竟没有Cookie,Session就根本没法用(不知道为什么?查查去)。Flask中使用Cookie也很简单:

from flask import Flask,url_for,request,render_template,redirect,session,make_response
import time
@app.route('/login', methods=['POST', 'GET'])
def login():
    response = None
    if request.method == 'POST':
        if request.form['user'] == 'admin':
            session['user'] = request.form['user']
            response = make_response('Admin login successfully!')
            response.set_cookie('login_time', time.strftime('%Y-%m-%d %H:%M:%S'))
        ...

    else:
        if 'user' in session:
            login_time = request.cookies.get('login_time')
            response = make_response('Hello %s, you logged in on %s' % (session['user'], login_time))
        ...

    return response
app.secret_key = '123456'
if __name__ == "__main__":
    app.run(debug=True)

这次我们引入了”time”模块来获取当前系统时间。我们在返回响应时,通过”response.set_cookie()”函数,来设置Cookie项,之后这个项值会被保存在浏览器中。这个函数的第三个参数(max_age)可以设置该Cookie项的有效期,单位是秒,不设的话,在浏览器关闭后,该Cookie项即失效。

在请求中,”request.cookies”对象就是一个保存了浏览器Cookie的字典,使用其”get()”函数就可以获取相应的键值。

图片.png

11.错误处理

使用”abort()”函数可以直接退出请求,返回错误代码:

rom flask import Flask,abort

app = Flask(__name__)
@app.route('/error')
def error():
    abort(404)

if __name__ == "__main__":
    app.run(debug=True)

上例会显示浏览器的404错误页面。有时候,我们想要在遇到特定错误代码时做些事情,或者重写错误页面,可以用下面的方法:

rom flask import Flask,abort

app = Flask(__name__)

@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404
if __name__ == "__main__":
    app.run(debug=True)
图片.png

不过,在实际开发过程中,我们并不会经常使用”abort()”来退出,常用的错误处理方法一般都是异常的抛出或捕获。装饰器”@app.errorhandler()”除了可以注册错误代码外,还可以注册指定的异常类型。让我们来自定义一个异常:

class InvalidUsage(Exception):
    status_code = 400

    def __init__(self, message, status_code=400):
        Exception.__init__(self)
        self.message = message
        self.status_code = status_code


@app.errorhandler(InvalidUsage)
def invalid_usage(error):
    response = make_response(error.message)
    response.status_code = error.status_code
    return response
@app.route('/exception')
def exception():
    raise InvalidUsage('No privilege to access the resource', status_code=403)
if __name__ == "__main__":
    app.run(debug=True)
图片.png

我们在上面的代码中定义了一个异常”InvalidUsage”,同时我们通过装饰器”@app.errorhandler()”修饰了函数”invalid_usage()”,装饰器中注册了我们刚定义的异常类。这也就意味着,一但遇到”InvalidUsage”异常被抛出,这个”invalid_usage()”函数就会被调用。

12.url重定向

Flask的URL规则是基于Werkzeug的路由模块。模块背后的思想是基于 Apache 以及更早的 HTTP 服务器主张的先例,保证优雅且唯一的 URL。

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

访问第一个路由不带/时,Flask会自动重定向到正确地址。
访问第二个路由时末尾带上/后Flask会直接报404 NOT FOUND错误。

重定向”redirect()”函数的使用在上面例子中已有出现。作用就是当客户端浏览某个网址时,将其导向到另一个网址。常见的例子,比如用户在未登录时浏览某个需授权的页面,我们将其重定向到登录页要求其登录先。

from flask import session, redirect
 
@app.route('/')
def index():
    if 'user' in session:
        return 'Hello %s!' % session['user']
    else:
        return redirect(url_for('login'), 302)

“redirect()”的第二个参数时HTTP状态码,可取的值有301, 302, 303, 305和307,默认即302(为什么没有304?留给大家去思考)。
转载来源:思诚之道

作者:BerL1n
链接:https://www.jianshu.com/p/6452596c4edb
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。