当前位置: 首页 > news >正文

ASP.NET Core 过滤器 使用依赖项注入

        过滤器是 ASP.NET Core 中的特殊组件,允许我们在请求管道的特定阶段控制请求的执行。这些过滤器在中间件执行后以及 MVC 中间件匹配路由并调用特定操作时发挥作用。

        简而言之,过滤器提供了一种在操作级别自定义应用程序行为的方法。它们就像检查点,允许我们执行特定任务,例如异常处理、缓存或添加自定义响应标头。

        当请求到达某个操作时,过滤器可以收集有关已选择哪个操作以及关联的路由数据的信息。此信息可用于做出决策并执行特定于该特定操作或控制器的操作。

        通过使用过滤器,我们在处理请求时拥有更多的控制权和灵活性。我们可以根据我们的要求实现自定义逻辑并将特定行为应用于不同的操作或控制器。这有助于在 ASP.NET Core 应用程序中实现更加定制和高效的请求处理流程。

过滤器的类型

        ASP.NET Core 提供了一组预定义的过滤器类型,允许我们在操作或控制器的上下文中控制请求的执行。这些过滤器有不同的用途,可以根据我们的要求进行定制。

以下是 ASP.NET Core 中预定义过滤器的主要类型:

  1. AuthorizationFilter:此过滤器在路由执行开始时执行。它确定是否允许用户访问该路由或执行请求的操作。

  2. ResourceFilter:该过滤器在授权后运行,可用于绕过剩余管道的执行。如果预处理的响应可用,我们想要发送缓存的响应,从而跳过管道的其余部分,那么它非常有用。

  3. ActionFilter:动作过滤器在动作执行之前和之后运行。它提供了一种用自定义逻辑包围操作或在执行操作之前和之后执行其他任务的方法。

  4. ResultFilter:此过滤器在操作生成结果之前和之后执行。它允许我们用附加行为包围结果执行或根据结果执行某些操作。

  5. ExceptionFilter:每当操作或控制器中抛出未捕获的异常时,异常过滤器就会运行。它提供了一种处理异常并设计特定于操作或控制器的自定义错误响应的方法。

  6. ServiceFilter:此过滤器运行另一个过滤器,其类型在其自身内传递。当传递的过滤器类型注册为服务时使用它。它解决通过依赖项注入 (DI) 传递给过滤器类型的任何依赖项。

  7. TypeFilter:与服务过滤器类似,类型过滤器也运行未注册为服务的过滤器类型。它允许我们应用自定义过滤器而无需注册。

        这些预定义的过滤器类型使我们能够灵活地控制请求执行过程,并在 ASP.NET Core 应用程序的管道的不同阶段添加特定行为。

过滤器范围 

        ASP.NET Core 中过滤器的范围取决于它们附加到 MVC 管道的方式。这使我们能够控制过滤器执行的时间和地点。过滤器的主要范围分为三个:

  1. 特定于操作的范围:过滤器可以应用于控制器内的特定操作方法。通过使用过滤器属性修饰操作,过滤器将仅在选择该特定操作来处理传入请求时执行。

  2. 特定于控制器的范围:过滤器也可以应用于整个控制器类。当过滤器应用于控制器级别时,它将针对该控制器内的所有操作执行。

  3. 全局范围:全局过滤器应用于路由中间件匹配和拾取的每条路由。这些过滤器被注册为全局过滤器,并且无论特定操作或控制器如何都会被执行。但是,如果没有为请求选择端点,则不会执行任何过滤器。

        要将过滤器注册为全局过滤器,可以将其添加到 Startup 类中的 ConfigureServices() 方法内的过滤器数组中。这是一个例子:

services.AddControllers(options => {
    options.Filters.Add(typeof(ConsoleGlobalActionFilter));
});

        在此示例中,使用 Add(typeof(...)) 方法将 ConsoleGlobalActionFilter 注册为全局筛选器。这确保过滤器将应用于路由中间件匹配和处理的每个路由。

        通过控制过滤器的范围,我们可以精确地确定过滤器将在 ASP.NET Core 应用程序中应用的时间和位置,从而允许我们添加特定的行为并有效地控制请求执行过程。

如何创建简单的过滤器

有两种方法可以创建我们之前讨论的类型的过滤器。我们以创建ActionFilter为例:

  1. 实现接口:要创建根据操作执行进行操作的自定义操作过滤器,我们可以创建一个实现 IActionFilter 或 IAsyncActionFilter 接口的类。这些接口提供了我们可以重写以添加自定义逻辑的方法。通过实现这些接口,我们可以完全控制操作过滤器的行为。

  2. 扩展属性类:或者,我们可以创建一个扩展 ActionFilterAttribute 类的自定义操作过滤器类。ActionFilterAttribute 类已经实现了 IActionFilter 和 IAsyncActionFilter 接口。通过扩展此类,我们可以重写我们感兴趣的方法并添加自定义逻辑。这种方法提供了一种更方便的方法来创建操作过滤器,因为我们可以直接扩展基类并专注于实现必要的方法。

        同样,对于提到的其他类型的过滤器(如授权过滤器、资源过滤器、结果过滤器、异常过滤器),也有相应的可以实现的接口或可以扩展的属性类。

以下是上述过滤器的接口和属性类:

  • IAuthorizationFilter:可以通过AuthorizationFilterAttribute实现或扩展。

  • IResourceFilter:可以通过ResourceFilterAttribute实现或扩展。

  • IActionFilter:可以通过ActionFilterAttribute实现或扩展。

  • IResultFilter:可以通过ResultFilterAttribute实现或扩展。

  • IExceptionFilter:可以通过ExceptionFilterAttribute实现或扩展。

        其中一些过滤器附带有实现相应接口的属性类,使我们能够灵活地重写适合我们需要的特定方法。以下是一些过滤器的属性类:

  • ActionFilterAttribute:该属性类实现 IActionFilter 和 IAsyncActionFilter 接口。通过扩展这个类,我们可以直接继承这些接口的实现并重写我们感兴趣的方法。这使得我们可以根据我们的需求自定义动作过滤器的行为。

  • ExceptionFilterAttribute:该属性类实现 IExceptionFilter 接口。通过扩展此类并重写其方法,我们可以处理和自定义对操作或控制器中发生的异常的处理。这使我们能够控制异常的处理方式,并允许我们提供自定义错误响应。

  • ResultFilterAttribute:该属性类实现 IResultFilter 接口。通过扩展此类并重写其方法,我们可以在将操作生成的结果发送回客户端之前对其进行修改。这允许我们向结果添加额外的处理或转换。

  • FormatFilterAttribute:此属性类用于指定操作或控制器支持的响应格式。它有助于内容协商并根据客户的偏好选择适当的响应格式。

  • ServiceFilterAttribute:此属性类用于应用注册为服务的过滤器。它允许我们使用依赖项注入来解决过滤器所需的任何依赖项。当过滤器需要额外的服务或依赖项才能正常运行时,这会很有帮助。

  • TypeFilterAttribute:该属性类与ServiceFilterAttribute类似,但它允许我们应用未注册为服务的过滤器。我们可以直接指定过滤器类型及其依赖项。

            例如,ActionFilterAttribute类已经为我们实现了IActionFilter和IAsyncActionFilter接口,因此我们可以直接扩展ActionFilterAttribute类并重写我们感兴趣的方法。

using System; 
using System.Threading.Tasks; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.AspNetCore.Mvc.Filters; 
using Newtonsoft.Json;  public class AddResponseHeaderFilter : ActionFilterAttribute 
{     // Async method which can surround the action execution// Invoked before and after the action executionpublic asyn coverride Task OnActionExecutionAsync(         ActionExecutingContext context, ActionExecutionDelegate next)     {         // Access the request         context.HttpContext.Response.Headers.Add(             "X-MyCustomHeader", Guid.NewGuid().ToString());                  var result = await next.Invoke();                  // Access the response         Console.WriteLine(JsonConvert.SerializeObject(result.Result));     } 
}  [Route("api/[controller]")] 
[ApiController] 
public class HomeController : ControllerBase 
{     [AddResponseHeaderFilter]     [Route("")]     [HttpGet]     public IActionResult Index()     {         return Ok(new { Message = "I'm Alive" });     } 
}

        在此示例中,我们通过扩展ActionFilterAttribute创建了自定义AddResponseHeaderFilter类。此过滤器在执行操作之前和之后向 HTTP 响应添加自定义响应标头。它重写OnActionExecutionAsync方法,该方法允许我们自定义操作执行周围的行为。

        然后,我们使用 [ AddResponseHeaderFilter ] 属性将AddResponseHeaderFilter过滤器应用到HomeController类的Index操作。这可确保针对该特定操作执行过滤器。

        调用该操作时,过滤器使用Response.Headers.Add方法将自定义标头添加到响应中。然后它调用 next.Invoke() 方法来继续执行操作。执行操作后,它会访问响应结果并使用JsonConvert.SerializeObject 将其写入控制台。

        通过利用ActionFilterAttribute并创建自定义过滤器类,我们可以扩展和自定义 ASP.NET Core 请求管道的行为,以添加其他功能并根据我们的要求修改响应。

动作过滤器的实现
示例 1:实现 IActionFilter 的同步操作过滤器 

using Microsoft.AspNetCore.Mvc.Filters;namespace ActionFilters.Filters
{public class ActionFilterExample : IActionFilter{public void OnActionExecuting(ActionExecutingContext context){// Code executed before the action method executes}public void OnActionExecuted(ActionExecutedContext context){// Code executed after the action method executes}}
}

        在此示例中,我们创建一个名为 ActionFilterExample 的类,该类实现 IActionFilter 接口。该接口要求我们实现两个方法:OnActionExecuting 和 OnActionExecuted。OnActionExecuting 方法包含将在操作方法之前执行的代码,而 OnActionExecuted 方法包含将在操作方法之后执行的代码。 

示例 2:实现 IAsyncActionFilter 的异步操作过滤器 

using Microsoft.AspNetCore.Mvc.Filters;
using System.Threading.Tasks;namespace ActionFilters.Filters
{public class AsyncActionFilterExample : IAsyncActionFilter{public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){// Code executed before the action method executesvar result = await next();// Code executed after the action method executes}}
}

        在此示例中,我们创建一个名为 AsyncActionFilterExample 的类,该类实现 IAsyncActionFilter 接口。该接口要求我们实现 OnActionExecutionAsync 方法,该方法是动作过滤器的异步版本。该方法接收一个 ActionExecutingContext 和一个 ActionExecutionDelegate。ActionExecutionDelegate 代表管道中的下一个操作,我们可以等待它执行操作方法。通过这样做,我们可以在操作方法执行之前和之后运行代码。

        通过创建实现 IActionFilter 或 IAsyncActionFilter 的类,我们可以定义要在 ASP.NET Core 中的操作方法之前和之后执行的自定义逻辑。这些操作过滤器提供了一种向请求管道添加附加行为并根据需要修改请求或响应的方法。

 操作过滤器的范围
可以在不同范围级别添加操作过滤器:全局、操作和控制器。

1. 全局范围

要全局使用操作过滤器,您可以在ConfigureServices方法的AddControllers()方法中注册它:

public void ConfigureServices(IServiceCollection services)
{services.AddControllers(config =>{config.Filters.Add(new GlobalFilterExample());});
}

在.NET 6及更高版本中,由于缺少 Startup 类,您可以使用 Program 类: 

builder.Services.AddControllers(config => 
{ config.Filters.Add( new GlobalFilterExample()); 
});

通过将过滤器添加到全局范围,它将应用于整个应用程序中的所有操作方法。

2. 操作和控制器范围

如果要在操作或控制器级别使用操作过滤器,则需要在ConfigureServices方法中将其注册为IoC容器中的服务:

services.AddScoped<ActionFilterExample>(); 
services.AddScoped<ControllerFilterExample>();

在 .NET 6 及更高版本中: 

builder.Services.AddScoped<ActionFilterExample>(); 
builder.Services.AddScoped<ControllerFilterExample>();

        通过将操作过滤器注册为服务,您可以通过使用相应的过滤器属性装饰它们,有选择地将其应用到特定的操作方法或控制器。

        最后,要使用在操作或控制器级别注册的过滤器,您需要使用ServiceFilter属性将其应用到相应的控制器或操作方法之上
namespace AspNetCore.Controllers
{[ServiceFilter(typeof(ControllerFilterExample))][Route("api/[controller]")][ApiController]public class TestController : ControllerBase{[HttpGet][ServiceFilter(typeof(ActionFilterExample))]public IEnumerable<string> Get(){return new string[] { "example", "data" };}}
}

        通过使用[ServiceFilter]属性并指定过滤器类型,您可以将注册的过滤器(ControllerFilterExample 和 ActionFilterExample)分别与控制器和操作方法关联起来。

        这允许将过滤器应用于指定的范围,控制执行流程并在执行操作方法之前和之后提供附加功能。
        以这种方式使用ServiceFilter属性可以帮助您利用依赖项注入的优势,并将所需的过滤器应用到 ASP.NET 应用程序中的特定控制器和操作方法。

调用顺序
我们的过滤器的执行顺序如下:

我们可以通过向ServiceFilter属性添加一个名为Order 的附加属性来更改多个过滤器的调用顺序 

namespace AspNetCore.Controllers
{[ServiceFilter(typeof(ControllerFilterExample), Order=2)][Route("api/[controller]")][ApiController]public class TestController : ControllerBase{[HttpGet][ServiceFilter(typeof(ActionFilterExample), Order=1)]public IEnumerable<string> Get(){return new string[] { "example", "data" };}}
}

        在本例中,ControllerFilterExample将在ActionFilterExample之前执行,因为它们的顺序值分别为 2 和 1。通过为过滤器分配不同的顺序值,您可以控制它们的执行顺序。

您还可以将多个过滤器应用于同一操作方法并定义它们的执行顺序:
[HttpGet] 
[ServiceFilter(typeof(ActionFilterExample), Order=2)] 
[ServiceFilter(typeof(ActionFilterExample2), Order=1)] 
public IEnumerable<string> Get() 
{ return new string[] { "example", "data ” }; 
}

         在这种情况下,ActionFilterExample2将根据其顺序值分别为 1 和 2,在ActionFilterExample之前执行。

        通过指定每个过滤器的顺序,您可以对执行顺序进行细粒度控制,并且可以应用具有不同优先级的多个过滤器以在 ASP.NET 应用程序中实现所需的行为。

使用操作过滤器改进代码
        为了使用操作过滤器改进代码,让我们重点关注存储库中AppStart文件夹中起始项目的Controllers文件夹中的MoveController类。该控制器包含所有 CRUD 操作的实现。

尽管我们的操作已经干净且可读,但由于全局异常处理,我们可以进一步增强它们。

需要注意的一件重要事情是我们的Movie模型继承自IEntity接口:

[Table("Movie")]
public class Movie: IEntity
{[Key]public Guid Id { get; set; }[Required(ErrorMessage = "Name is required")]public string Name { get; set; }[Required(ErrorMessage = "Genre is required")]public string Genre { get; set; }[Required(ErrorMessage = "Director is required")]public string Director { get; set; }
}

         现在,让我们关注POSTPUT操作的验证代码。通过将适当的操作过滤器应用于POSTPUT操作,我们可以消除这些操作中显式验证代码的需要,使它们更加简洁和可维护。

使用操作过滤器进行验证
        为了改进POST和PUT操作中的验证代码,我们可以使用操作过滤器。通过将验证逻辑提取到自定义操作过滤器中,我们可以使代码更具可重用性并保持操作更清晰。

        首先,我们在解决方案资源管理器中创建一个名为“ActionFilters”的新文件夹。在该文件夹中,创建一个名为ValidationFilterAttribute的新类:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;namespace ActionFilters.ActionFilters
{public class ValidationFilterAttribute : IActionFilter{public void OnActionExecuting(ActionExecutingContext context){var param = context.ActionArguments.SingleOrDefault(p => p.Value is IEntity);if (param.Value == null){context.Result = new BadRequestObjectResult("Object is null");return;}if (!context.ModelState.IsValid){context.Result = new UnprocessableEntityObjectResult(context.ModelState);}}public void OnActionExecuted(ActionExecutedContext context){          }}
}

         在ValidationFilterAttribute类中,我们重写OnActionExecuting方法来执行验证逻辑。我们检查操作参数是否为 IEntity 类型,如果为 null,则返回BadRequestObjectResult。此外,如果 ModelState 无效,我们将返回UnprocessableEntityObjectResult

        接下来,让我们在ConfigureServices方法中将此操作过滤器注册为服务:

public void ConfigureServices(IServiceCollection services)
{services.AddDbContext<MovieContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("sqlConString")));services.AddScoped<ValidationFilterAttribute>();services.AddControllers();
}

对于.NET 6,我们需要在 Program 类中使用构建器变量: 

builder.Services.AddDbContext<MovieContext>(options => options.UseSqlServer(Configuration.GetConnectionString( "sqlConString" ))); builder.Services.AddScoped<ValidationFilterAttribute>(); builder.Services.AddControllers();

最后,从 POST 和 PUT 操作中删除验证代码,并将ValidationFilterAttribute作为服务应用:

[HttpPost]
[ServiceFilter(typeof(ValidationFilterAttribute))]
public IActionResult Post([FromBody] Movie movie)
{_context.Movies.Add(movie);_context.SaveChanges();return CreatedAtRoute("MovieById", new { id = movie.Id }, movie);
}[HttpPut("{id}")]
[ServiceFilter(typeof(ValidationFilterAttribute))]
public IActionResult Put(Guid id, [FromBody] Movie movie)
{var dbMovie = _context.Movies.SingleOrDefault(x => x.Id.Equals(id));if (dbMovie == null){return NotFound();}dbMovie.Map(movie);_context.Movies.Update(dbMovie);_context.SaveChanges();return NoContent();
}

        通过将 ValidationFilterAttribute 应用为服务过滤器,我们无需在操作中使用验证代码。代码现在更干净、更具可读性。此外,只要我们的模型类继承自 IEntity 接口,此验证逻辑就可以重用。

        为了确保操作过滤器的验证优先于 [ApiController] 属性的默认验证行为,我们需要禁止默认验证。在 Startup 类(或 .NET 6 的 Program 类)中,添加以下配置:
services.Configure<ApiBehaviorOptions>(options => 
{ options.SuppressModelStateInvalidFilter = true ; 
});

         此配置可确保返回操作过滤器的验证结果(例如,UnprocessableEntity),而不是返回验证错误的默认BadRequest结果。

        通过这些更改,我们的验证过滤器已准备好进行测试。

动作过滤器中的依赖注入
        为了消除通过 ID 从数据库中获取电影并在 GetById、DELETE 和 PUT 操作中检查其是否存在的代码重复,我们可以创建一个新的操作过滤器来执行此任务。我们还将使用依赖注入将 MovieContext 注入到动作过滤器中。

让我们在 ActionFilters 文件夹中创建一个名为 ValidateEntityExistsAttribute<T> 的新操作过滤器类:

using System; 
using System.Linq; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.AspNetCore.Mvc.Filters;  namespace ActionFilters.ActionFilters 
{     public class ValidateEntityExistsAttribute<T> : IActionFilter where T : class, IEntity     {         private readonly MovieContext _context;          public ValidateEntityExistsAttribute(MovieContext context)         {             _context = context;         }          public void OnActionExecuting(ActionExecutingContext context)         {             Guid id = Guid.Empty;              if (context.ActionArguments.ContainsKey("id"))             {                 id = (Guid)context.ActionArguments["id"];             }             else             {                 context.Result = new BadRequestObjectResult("Bad id parameter");                 return;             }              var entity = _context.Set<T>().SingleOrDefault(x => x.Id.Equals(id));             if (entity == null)             {                 context.Result = new NotFoundResult();             }             else             {                 context.HttpContext.Items.Add("entity", entity);             }         }          public void OnActionExecuted(ActionExecutedContext context)         {         }     } 
} 

        在ValidateEntityExistsAttribute<T>类中,我们使用依赖注入通过构造函数注入MovieContext 。该类是通用的,以便它可以重用于我们项目中的任何模型。在OnActionExecuting方法中,我们从操作上下文中获取 ID 参数并检查该实体是否存在于数据库中。如果找到实体,我们会将其存储在HttpContext .Items 集合中,以便稍后在操作方法中使用。

        接下来,让我们在ConfigureServices方法中注册操作过滤器:

 services.AddScoped<ValidateEntityExistsAttribute<Movie>>();

        对于 .NET 6,我们在 Program 类中使用构建器变量: 

builder.Services.AddScoped<ValidateEntityExistsAttribute<Movie>>();

最后,修改我们的操作以将 ValidateEntityExistsAttribute 应用为服务过滤器:

[HttpGet("{id}", Name = "MovieById")] [ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))] 
public IActionResult Get(Guid id) 
{     var dbMovie = HttpContext.Items["entity"] as Movie;      return Ok(dbMovie); 
}  [HttpPut("{id}")] 
[ServiceFilter(typeof(ValidationFilterAttribute))] 
[ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))] 
public IActionResult Put(Guid id, [FromBody]Movie movie) 
{     var dbMovie = HttpContext.Items["entity"] as Movie;      dbMovie.Map(movie);      _context.Movies.Update(dbMovie);     _context.SaveChanges();      return NoContent(); 
}  [HttpDelete("{id}")] 
[ServiceFilter(typeof(ValidateEntityExistsAttribute<Movie>))] 
public IActionResult Delete(Guid id) 
{     var dbMovie = HttpContext.Items["entity"] as Movie;      _context.Movies.Remove(dbMovie);     _context.SaveChanges();      return NoContent(); 
} 

        通过应用ValidateEntityExistsAttribute<Movie>作为服务过滤器,我们确保从数据库中获取电影实体并通过HttpContext.Items在操作方法中可用。这消除了在获取实体并检查其存在时重复代码的需要。

        通过这些更改,我们的操作更加清晰、更具可读性,并且通过 ID 获取实体的代码现在可以重用。

相关文章:

ASP.NET Core 过滤器 使用依赖项注入

过滤器是 ASP.NET Core 中的特殊组件&#xff0c;允许我们在请求管道的特定阶段控制请求的执行。这些过滤器在中间件执行后以及 MVC 中间件匹配路由并调用特定操作时发挥作用。 简而言之&#xff0c;过滤器提供了一种在操作级别自定义应用程序行为的方法。它们就像检查点&#…...

2024年的网创之路应该这样走才对

2024年的网创之路应该这样走才对 大家都知道这两年经济环境不好&#xff0c;钱不好挣&#xff0c;对于普通人&#xff0c;只有一条出路&#xff0c;就是学某项技能&#xff0c;然后死磕&#xff0c;不能提升某项技能的项目&#xff0c;打死也不做&#xff0c;因为多数项目都是…...

ssh异常报错:Did not receive identification string from

一、问题描述 某次外出在异地工作场所xshell炼乳远程服务器时&#xff0c;报错&#xff1a;Connection closed by foreign host. D&#xff0c;服务器查看secure日志或sshd服务状态会显示&#xff1a;id not receive identification string from client_ip; 二、分析处理 1&a…...

MIDI码深度解析

MIDI 协议即数字音乐接口&#xff08;Musical Instrument Digital Interface&#xff09;&#xff0c;是电子乐器、合成器等演奏设备之间的一种即时通信协议&#xff0c;用于硬件之间的实时演奏数据传递。如果理解还不够深刻&#xff0c;官方如下解释&#xff1a; 常用midi硬件…...

小红书如何做混部?

作者&#xff1a;宋泽辉&#xff08;小红书&#xff09;、张佐玮&#xff08;阿里云&#xff09; 编者按&#xff1a; Koordinator 是一个开源项目&#xff0c;是基于阿里巴巴内部多年容器调度、混部实践经验孵化诞生&#xff0c;是行业首个生产可用、面向大规模场景的开源混…...

[PHP]严格类型

PHP: 类型声明 - Manual...

作为程序员,你必须学会Maven

资源领取在末尾. Maven 是一款旨在简化 Java 开发流程的管理工具&#xff0c;它的主要功能包括&#xff1a; 1. 项目管理&#xff1a;Maven 提供了一种项目对象模型(Project Object Model, POM)&#xff0c;用于管理项目的构建、报告和文档。它允许开发者通过少量代码…...

UDF学习(三)数据访问宏

数据访问宏一 网格节点相关宏** NODE_X (v) 节点v的x方向的坐标 (Node *v) NODE_Y (v) 节点v的y方向的坐标 (Node *v) NODE_Z (v) 节点v的z方向的坐标 (Node *v) F_NODE (f,t,n) 获取节点 (face_t f, Thread *t, int n 节点索引号) F_NNODES(f,t) 获取面上的节点数量 (…...

Web3技术革新:重新定义在线体验

互联网的不断演进塑造了我们的数字生活&#xff0c;而Web3技术的涌现正带来一场前所未有的变革。本文将深入探讨Web3技术的创新&#xff0c;以及它如何重新定义和提升我们的在线体验。 Web3技术的基本概念 Web3是互联网的第三个时代&#xff0c;它将去中心化、区块链、智能合约…...

从前端Vue到后端Spring Boot:接收JSON数据的正确姿势

目录 一、前端Vue发送JSON数据二、后端Spring Boot接收JSON数据三、常见错误和问题四、总结 在现代Web开发中&#xff0c;前后端分离已成为一种趋势&#xff0c;Vue和Spring Boot也成为了其中最流行的前后端框架。在Vue前端向Spring Boot后端发送数据时&#xff0c;常常需要将数…...

nvm 工具使用介绍

目录 1.背景2.nvm介绍3.下载和安装4.配置环境变量5.配置淘宝镜像5.1 方式一:直接执行命令5.2 方式二:修改配置文件6.常用命令7.总结下载地址: https://github.com/coreybutler/nvm-windows/releases1.背景 在工作中,我们可能需要同时进行2个或者多个前端项目的开发,每个项…...

Shell 入门_1

Shell概述 本次课程主要包含内容: Shell脚本入门Shell变量Shell内置命令Shell运算符与执行运算命令流程控制语句Shell函数Shell重定向Shell好用的工具, cut sed awk sort大厂常见企业面试题 Shell脚本入门 疑问 linux系统是如何操作计算机硬件CPU,内存,磁盘,显示器等? 答…...

力扣hot100 柱状图中最大的矩形 单调栈

Problem: 84. 柱状图中最大的矩形 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考地址 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public static int largestRectangleArea(int[] height){Stack&l…...

018 用户交互Scanner

什么是Scanner对象 next()方法 // 声明输入对象 Scanner scanner new Scanner(System.in);System.out.println("next()方法接收&#xff1a;"); if (scanner.hasNext()) {// 输入 Hello worldString str1 scanner.next();// 输出 HelloSystem.out.println(str1); }…...

华为HCIE课堂笔记第十七章 广域网互联技术

第十七章 广域网互联技术 17.1 GRE VPN GRE VPN用于分支与分支通过私网地址互联&#xff0c;通过在私网报文上添加一个GRE的头部&#xff0c;以及添加一层外层的IP头部&#xff0c;通过外层头部中的目IP地址使得报文到达隧道对端接口&#xff0c;并解封装得到原始的私网报文…...

代码随想录算法训练营第17天(二叉树5)| 找树左下角的值二叉树的路径总和从中序与后序遍历序列构造二叉树从前序与中序遍历序列构造二叉树

513.找树左下角的值 leetcode题目地址 题目链接/文章讲解/视频讲解 如果使用递归法&#xff0c;如何判断是最后一行&#xff1a; 其实就是深度最大的叶子节点一定是最后一行。 //迭代法 class Solution { public:int findBottomLeftValue(TreeNode* root) {queue<TreeNod…...

代码随想录 Leetcode106. 从中序与后序遍历序列构造二叉树

题目&#xff1a; 代码&#xff08;首刷看解析 2024年1月30日&#xff09;&#xff1a; class Solution { public:TreeNode* recursion(vector<int>& inorder, vector<int>& postorder, int longthOfLeft, int longthOfRight) {if (postorder.size() 0) …...

Log4j Log4j2

前言 今天抽时间来把这个日志框架学学&#xff0c;毕竟经常用&#xff0c;虽然不用自己写&#xff0c;但是书到用时方恨少&#xff0c;技多不压身。而且最近我的 GUI 软件中有一个关于日志问题的希望学完能够感觉解决掉。 Log4j & Log4j2 Log4j2 是 Log4j 的升级版&#x…...

C语言——如何进行文件操作

大家好&#xff0c;我是残念&#xff0c;希望在你看完之后&#xff0c;能对你有所帮助&#xff0c;有什么不足请指正&#xff01;共同学习交流 本文由&#xff1a;残念ing原创CSDN首发&#xff0c;如需要转载请通知 个人主页&#xff1a;残念ing-CSDN博客&#xff0c;欢迎各位→…...

python中for循环的几个现象

1. 运行如下代码 l [{}, {}, {}] for k in l:k[1] 1 print(l) 输出为 [{1: 1}, {1: 1}, {1: 1}]2. 运行如下代码 l [{}, {}, {}] for k in l:k {1:1} print(l) 输出为 [{}, {}, {}] 3. 运行如下代码 l [1,2,3] for k in l:k k * 2 print(l)输出为 [1, 2, 3…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...

动态规划-1035.不相交的线-力扣(LeetCode)

一、题目解析 光看题目要求和例图&#xff0c;感觉这题好麻烦&#xff0c;直线不能相交啊&#xff0c;每个数字只属于一条连线啊等等&#xff0c;但我们结合题目所给的信息和例图的内容&#xff0c;这不就是最长公共子序列吗&#xff1f;&#xff0c;我们把最长公共子序列连线起…...

【R语言编程——数据调用】

这里写自定义目录标题 可用库及数据集外部数据导入方法查看数据集信息 在R语言中&#xff0c;有多个库支持调用内置数据集或外部数据&#xff0c;包括studentdata等教学或示例数据集。以下是常见的库和方法&#xff1a; 可用库及数据集 openintro库 该库包含多个教学数据集&a…...

在MobaXterm 打开图形工具firefox

目录 1.安装 X 服务器软件 2.服务器端配置 3.客户端配置 4.安装并打开 Firefox 1.安装 X 服务器软件 Centos系统 # CentOS/RHEL 7 及之前&#xff08;YUM&#xff09; sudo yum install xorg-x11-server-Xorg xorg-x11-xinit xorg-x11-utils mesa-libEGL mesa-libGL mesa-…...

MySQL 数据库深度剖析:事务、SQL 优化、索引与 Buffer Pool

在当今数据驱动的时代&#xff0c;数据库作为数据存储与管理的核心&#xff0c;其性能与可靠性至关重要。MySQL 作为一款广泛使用的开源数据库&#xff0c;在众多应用场景中发挥着关键作用。在这篇博客中&#xff0c;我将围绕 MySQL 数据库的核心知识展开&#xff0c;涵盖事务及…...

Continue 开源 AI 编程助手框架深度分析

Continue 开源 AI 编程助手框架深度分析 一、项目简介 Continue 是一个模块化、可配置、跨平台的开源 AI 编程助手框架&#xff0c;目标是让开发者能在本地或云端环境中&#xff0c;快速集成和使用自定义的 LLM 编程辅助工具。它通过支持 VS Code 与 JetBrains 等主流 IDE 插件…...