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 execution
    public 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 获取实体的代码现在可以重用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/360387.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JSP仓储管理系统myeclipse定制开发SQLServer数据库网页模式java编程jdbc

一、源码特点 JSP仓储管理系统系统是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库 &#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为SQLServer2008&#x…

Java关于Excel文件的导入导出

人生如梦 荣华富贵 如木槿之花 朝荣夕逝 需求 导出&#xff1a; 能够将库表内的数据导出多个Excel表&#xff0c;并且生成一个压缩包&#xff0c;提供用户下载导入&#xff1a; 能够将一个压缩包内的多个Excel表解压&#xff0c;并获取表内的所有数据 FileUtils 工具类 publi…

uniapp多格式文件选择(APP,H5)

uniapp多格式文件选择&#xff08;APP&#xff0c;H5&#xff09; 背景实现代码实现运行结果注意事项 尾巴 背景 从手机选择文件进行上传是移动端很常见的需求&#xff0c;在原生开发时由于平台专一性很容易实现。但是用uniapp开发官方提供的API在APP平台只能选择图片和视频&a…

C语言如何理解 c=a,b;?

一、问题 对于表达式 ca,b;和 d(a,b);该如何进⾏理解&#xff1f;它们的值都是怎样的&#xff1f; 二、解答 在C语⾔中&#xff0c;逗号有两个作⽤&#xff0c;⼀是⽤来分隔函数参数&#xff0c;⼆是作为逗号运算符。本题主要考虑的是逗号运算符&#xff0c;根据逗号运算符的规…

第七篇:node中间件详解

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 &#x1f4d8; 引言&#xff1a; &#…

nacos启动成功,程序连接失败

问题&#xff1a;nacos服务器启动成功后可以访问&#xff0c;但是程序连接却超时 解决&#xff1a;检查端口&#xff0c;2.0以上的版本需要开放的端口一共是三个&#xff01;&#xff01; 8848 9848 9849 找了很久是因为后面两个端口没有开放&#xff0c;原因是因为2.0以上…

控制台的过滤条请求类型被变成下拉选想变成一行

控制台的请求类型被变成下拉选了找到设置---实现----不选Redesign of the filter bar in the Network Panel(重新设计网络面板中的过滤条) 问题如图&#xff1a; 希望是如图&#xff1a; 解决方案:

空间数据分析和空间统计工具库PySAL入门

空间数据分析是指利用地理信息系统(GIS)技术和空间统计学等方法&#xff0c;对空间数据进行处理、分析和可视化&#xff0c;以揭示数据之间的空间关系和趋势性&#xff0c;为决策者提供有效的空间决策支持。空间数据分析已经被广泛运用在城市规划、交通管理、环境保护、农业种植…

Nacos服务注册源码解析

简介&#xff1a;本文将以图文方式详述 Spring Cloud Alibaba 技术体系之 Nacos 服务注册源码解析。 目录 一、Nacos 服务架构 二、Nacos 服务注册流程图&#xff08;源码级别&#xff09; 1. 注册中心核心工作流程 三、Nacos 服务注册源码解析 1. 服务注册 1.1 客户端服…

代码随想录 Leetcode110.平衡二叉树

题目&#xff1a; 代码(首刷看解析 2024年1月30日&#xff09;&#xff1a; class Solution { public:int depth(TreeNode* root) {if (root nullptr) return 0;int leftHeight depth(root->left);if (leftHeight -1) return -1;int rightHeight depth(root->right)…

【Linux C | 网络编程】getsockname 和 getpeername函数详解及C语言例子

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

【遥感专题系列】遥感影像信息提取之——人工目视解译

​遥感影像通过亮度值或像元值的高低差异&#xff08;反映地物的光谱信息&#xff09;及空间变化&#xff08;反映地物的空间信息&#xff09;来表示不同地物的差异&#xff0c;这是区分不同影像地物的物理基础。 ​人工解译是目前国内使用最多的一种影像提取方法&#xff0c;如…

Android studio环境搭建过程异常

异常&#xff1a;Connect timed out 创建新项目时&#xff0c;提示time out 解决方案&#xff1a;修改gradle下载地址&#xff0c;使用国内镜像地址 distributionUrlhttps\://services.gradle.org/distributions/gradle-8.2-bin.zip修改成distributionUrlhttps\://mirrors.c…

海外拓展必备:精细规划的出海策略,实现市场全球化的第一步

随着全球化的深入&#xff0c;越来越多的企业开始将目光投向海外市场&#xff0c;以寻求更广阔的发展空间。然而&#xff0c;进入新的市场并进行有效的海外营销并非易事&#xff0c;需要经过精心策划和系统性的执行。本文Nox聚星将和大家详细探讨出海营销的第一步应该如何着手&…

初始化和赋值

列表初始化 #include <iostream>struct test{int a; };class object{ public:object(int v10):a{v}{}int a; };int main(){test t1;test t2{};std::cout<<t1.a<<std::endl;std::cout<<t2.a<<std::endl;object b2{11};std::cout<<b2.a<…

泛谈一下数字化技能的学习,SPSS、Stata还是Python?技术、业务+表达、展现!

1.本科、专科上学时对于这些偏数学类的课程还是要好好学习 应知乎、小红书、CSDN很多年轻朋友、同学们的邀请&#xff0c;今天我泛谈一下数字化技能的学习。很多学生在本科或专科上学时代学过统计学、计量经济学、机器学习、数据分析、统计分析、数据挖掘、量化建模等一门或多…

宏集物联网HMI、工业树莓派、触控一体机,超值优惠限时放送

亲爱的客户朋友们&#xff1a; 新春佳节将至&#xff0c;为回馈广大新老客户们一年来的支持与厚爱&#xff0c;宏集科技推出一场盛大的新春促销活动&#xff01; 我们精选了一系列宏集科技优质产品&#xff0c;包括工业树莓派、物联网HMI、触控一体机等&#xff0c;为您的工程…

React通用后台模板

一. 项目初始化 1. 创建项目 环境 npm init vite 打开package.json,参考以下各模块版本: "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^7.2.8", …

探索Allure Report:提升自动化测试效率的秘密武器!

一.使用 Allure2 运行方式-Python # --alluredir 参数生成测试报告。 # 在测试执行期间收集结果 pytest [测试用例/模块/包] --alluredir./result/ (—alluredir这个选项 用于指定存储测试结果的路径)# 生成在线的测试报告 allure serve ./result二.使用 Allure2 运行方式-Ja…