ASP.NET

发布时间 2023-09-05 16:07:12作者: 朦朦胧胧的月亮最美好
筛选器Filters:

在处理请求和生成响应的过程中插入逻辑

Authorization Filters(授权筛选器)

[Authorize] 用于限制只有经过身份验证的用户才能访问某个动作或控制器。

[Authorize]
public class ProtectedController : Controller
{
  public IActionResult Index()
  {
      return View();
  }
}
var claims = new List<Claim>
{
  new Claim(ClaimTypes.Name, "username"),
  new Claim("IsAdmin", "true")
};

var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);

await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
services.AddAuthorization(options =>
{
  options.AddPolicy("AdminOnly", policy =>policy.RequireClaim("IsAdmin", "true"));
});

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
  options.LoginPath = "/Admin/Adminor/Login";
});

app.UseAuthentication();
app.UseAuthorization();
[Authorize(Policy = "AdminOnly")]
public class AdminController : Controller
{
  // ...
}

Action Filters(动作筛选器)在执行控制器动作之前或之后执行代码。例如,[HttpGet][HttpPost] 是用于指定 HTTP 请求类型的动作筛选器。另一个常见的例子是 OnActionExecutingOnActionExecuted 方法,允许你在执行动作之前和之后执行自定义代码。

Result Filters(结果筛选器)在控制器动作执行之后,但在结果返回给客户端之前执行代码。OnResultExecutingOnResultExecuted 方法允许你在处理结果之前和之后执行自定义操作。

ActionFilterAttribute 是 ASP.NET Core 中用于创建自定义操作过滤器的基类,而 ActionFilterResultFilter 都是继承自 ActionFilterAttribute 的类,用于实现不同类型的操作过滤器。

public class CustomActionFilter : ActionFilterAttribute

public override void OnActionExecuting(ActionExecutingContext context)
{
  // 在控制器动作执行之前执行的逻辑
  // 可以检查参数、验证权限等
  base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
  // 在控制器动作执行之后执行的逻辑
  // 可以修改结果、记录日志、执行清理等
  base.OnActionExecuted(context);
}
public override void OnResultExecuting(ResultExecutingContext context)
{
  // 在操作结果执行之前执行的逻辑
  // 可以修改结果、记录日志、添加响应头等
  base.OnResultExecuting(context);
}
public override void OnResultExecuted(ResultExecutedContext context)
{
  // 在操作结果执行之后执行的逻辑
  // 可以记录日志、执行清理等
  base.OnResultExecuted(context);
}

当控制器发生异常时,不会进入 OnResultExecuted 方法。异常会被捕获。

Exception Filters(异常筛选器):用于处理控制器动作中发生的异常。[HandleError] 是一个常见的异常筛选器,用于捕获并处理控制器中的异常。

using Microsoft.AspNetCore.Mvc.Filters;

public class CustomExceptionFilter : IExceptionFilter
{
  public void OnException(ExceptionContext context)
  {
      // 在异常发生时执行的逻辑
      var exception = context.Exception;
      // 可以记录日志、返回自定义错误页面或错误响应等

      context.ExceptionHandled = true; // 表示异常已被处理
  }
}
[CustomExceptionFilter]
public class MyController : Controller
{
  // 控制器动作
}
services.AddControllers(options =>
{
  options.Filters.Add(new CustomExceptionFilter());
});

Resource Filters(资源筛选器):在 ASP.NET Core 中引入的一种筛选器类型,用于在处理请求之前和之后执行一些全局的资源管理操作,例如处理响应头、响应内容等。

using Microsoft.AspNetCore.Mvc.Filters;
using System;

public class MyResourceFilter : IResourceFilter
{
  public void OnResourceExecuting(ResourceExecutingContext context)
  {
      // 在资源(如数据库连接、HTTP 上下文等)在控制器操作执行前被使用之前执行的逻辑
      Console.WriteLine("Resource is being used before action.");
  }

  public void OnResourceExecuted(ResourceExecutedContext context)
  {
      // 在控制器操作执行后,资源被释放之前执行的逻辑
      Console.WriteLine("Resource is being released after action.");
  }
}
[MyResourceFilter]
public class MyController : Controller
{
    // 控制器动作
}

 

builder,service和app
  • builder 用于配置主机环境,包括应用程序的配置、日志等。

  • builder.services 用于配置依赖注入容器中的服务,使服务能够在应用程序的其他部分中被注入和使用。

    public void ConfigureServices(IServiceCollection services)
  • builder.app 用于配置中间件管道,处理传入的 HTTP 请求。

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

    以上定义在startup.cs中,在program中这样引入

    CreateHostBuilder(args).Build().Run();
    IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
    WebApplication.CreateBuilder(args)
        .UseStartup<Startup>()
        .Build()
        .Run();

 

Task
  • Task.RunTask.Run 是一个静态方法,用于在当前线程池上启动一个新的任务,并在任务中运行指定的操作。它的使用非常简单,你只需要提供一个需要执行的操作,就可以自动创建和启动任务。

  • Task.StartTask.StartTask 类的实例方法,用于手动启动已经创建的任务。你需要先创建一个 Task 实例,然后调用 Start 方法来启动任务。

  • Task<int> task = new Task<int>(async () =>
    {
    Console.WriteLine("Task Thread Id: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(2000);
    return 42;
    });
    task.Start(); // 手动开始执行任务
    int result = await task;
    Console.WriteLine($"Result: {result}");

 

using

using 语句可以确保在使用完资源后,无论代码是否发生异常,都会自动释放资源,从而避免资源泄漏。

using 语句通常用于管理实现了 IDisposable 接口的对象,例如文件流、数据库连接等。

 

Ajax与model
  • 特点:不刷新整个页面异步获取数据。交互时,动态加载内容,例如通过下拉刷新、搜索框自动补全等。

  • 客户端渲染:当浏览器请求初始的骨架HTML页面,然后使用JavaScript等客户端技术通过Ajax请求从服务器获取数据,再将数据动态地插入到页面中,这被称为客户端渲染。前端页面在初始加载时只包含了基本的HTML结构,数据获取和渲染是在浏览器中通过客户端脚本完成的。

  • 更丰富的动态交互,减轻服务器负担,灵活性,较慢的初始加载速度,SEO挑战,性能在低性能设备上表现较差。

 

  • 特点:这种方式是为了分离数据和展示逻辑,使代码更加模块化和可维护。

  • 服务端渲染:当服务器在接收到请求后,将模型数据嵌入到视图模板中,然后生成完整的HTML页面并将其发送到客户端,这被称为服务端渲染。

  • 更快的初始加载速度,SEO友好,性能优势在低性能设备上更明显,较少的动态交互,更高的服务器负载。

     

    在实际开发中,也可以考虑采用混合渲染的方式,即使用服务端渲染来处理初始页面加载和重要内容,然后通过客户端渲染来实现更丰富的交互体验。

 

Elaticsearch日志存储

在将日志数据存储到Elasticsearch(ES)之前,将数据放入Kafka等消息队列系统中有一些优点和合理性。这种架构通常被称为日志流水线(Log Pipeline),以下是一些原因:解耦和异步处理,缓冲和流量控制,数据丢失防护,数据转换和预处理,灵活性

 

IWebHost和IHost

IWebHost 主要用于托管和管理 ASP.NET Core Web 应用程序,而 IHost 则适用于托管通用的应用程序,不仅限于 Web 应用。

 

IIS,WindowService,WebAPI

IIS 是一个 Web 服务器软件,用于托管和管理 Web 应用程序;Windows 服务是后台运行的应用程序;而 Web API 则用于构建 HTTP 服务,用于提供数据和功能。根据你的需求,可以选择适当的部署方式和技术。

 

WebSocket

在搭建聊天室时,选择使用TCP请求而不是HTTP请求是因为TCP(传输控制协议)和HTTP(超文本传输协议)具有不同的特性,适用于不同的场景。以下是选择TCP请求而不是HTTP请求的一些原因:

即时性:

TCP连接可以保持长时间,使得聊天室能够实时地传输消息,而不需要每次都建立新的连接,从而减少了延迟。

HTTP协议是无状态的,每次请求都需要建立新的连接,不如TCP适合实时聊天应用。

双向通信:

TCP支持全双工通信,客户端和服务器可以同时发送和接收数据。

HTTP请求通常是单向的,客户端发起请求,服务器响应,无法保持持续的双向通信。

自定义协议:

TCP提供更大的灵活性,允许你定义自己的数据格式和通信规则。

HTTP协议的数据格式和操作有一定的限制。

 

全双工通信: WebSocket支持在同一连接上同时进行双向通信,服务器可以主动向客户端推送数据,而不需要客户端显式地发起请求。

持久连接: 与HTTP请求-响应不同,WebSocket连接是持久的,一旦建立,可以保持活动状态,允许在任何时间点进行数据传输。

低延迟: 由于不需要频繁地建立和关闭连接,WebSocket通信通常具有低延迟,适用于实时应用场景。

协议支持: WebSocket协议是标准化的,支持多种编程语言和平台,使不同系统之间的通信更加方便。

 

using System.Net.WebSockets;
using Microsoft.AspNetCore.Mvc;

namespace WebSocketsSample.Controllers;

#region snippet_Controller_Connect
public class WebSocketController : ControllerBase
{
    [Route("/ws")]
    public async Task Get()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }
    #endregion
    
    private static async Task Echo(WebSocket webSocket)
    {
        var buffer = new byte[1024 * 4];
        var receiveResult = await webSocket.ReceiveAsync(
            new ArraySegment<byte>(buffer), CancellationToken.None);

        while (!receiveResult.CloseStatus.HasValue)
        {
            await webSocket.SendAsync(
                new ArraySegment<byte>(buffer, 0, receiveResult.Count),
                receiveResult.MessageType,
                receiveResult.EndOfMessage,
                CancellationToken.None);

            receiveResult = await webSocket.ReceiveAsync(
                new ArraySegment<byte>(buffer), CancellationToken.None);
        }

        await webSocket.CloseAsync(
            receiveResult.CloseStatus.Value,
            receiveResult.CloseStatusDescription,
            CancellationToken.None);
    }
}
app.UseWebSockets();
socket = new WebSocket(connectionUrl.value);
socket.onopen = function (event) {};
socket.onclose = function (event) {};
socket.onerror = function (event) {};
socket.onmessage = function (event) {// user event.data};
socket.send("Hello, Server!");

 

TCP

 

image-20230810140636328

 

 

import socket

# 创建一个TCP/IP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定地址和端口
server_address = ('localhost', 8888)
server_socket.bind(server_address)

# 开始监听
server_socket.listen(5)  # 最多允许5个等待连接的客户端

print("等待客户端连接...")

while True:
    # 等待客户端连接
    client_socket, client_address = server_socket.accept()
    
    print(f"与客户端 {client_address} 建立连接")
    
    # 接收数据
    data = client_socket.recv(1024)
    print(f"接收到的数据:{data.decode('utf-8')}")
    
    # 发送响应
    response = "Hello, client! This is the server."
    client_socket.send(response.encode('utf-8'))
    
    # 关闭连接
    client_socket.close()
import socket

# 创建一个TCP/IP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接服务器
server_address = ('localhost', 8888)
client_socket.connect(server_address)

# 发送数据
message = "Hello, server! This is the client."
client_socket.send(message.encode('utf-8'))

# 接收响应
response = client_socket.recv(1024)
print(f"服务器的响应:{response.decode('utf-8')}")

# 关闭连接
client_socket.close()

 

TCP(传输控制协议)是一种用于在计算机网络中进行可靠数据传输的协议。在讲解TCP建立的过程时,涉及到一些关键的概念,包括队列、bind、listen、accept和backlog。以下是TCP建立连接的基本过程及相关概念的解释:

  1. 队列(Queue): 在服务器端,当多个客户端请求连接时,服务器需要将这些连接请求存放在一个等待队列中,以便逐一进行处理。这个队列称为“连接请求队列”,“SYN队列”。

  2. Bind: 在服务器端创建一个套接字(socket)时,需要将该套接字绑定到一个特定的IP地址和端口号上。这个过程称为“绑定(bind)”。

  3. Listen: 一旦套接字被绑定到特定的IP地址和端口号,服务器就可以开始监听传入的连接请求。通过调用listen()函数,服务器将套接字置于监听状态,等待客户端的连接请求。

  4. Accept: 当服务器处于监听状态并接收到客户端的连接请求时,服务器将调用accept()函数来接受这个连接。accept()函数会返回一个新的套接字,用于在服务器和客户端之间进行通信。这个新的套接字是服务器用于与该特定客户端之间进行数据交换的通道。

  5. Backlog: listen()函数的参数中通常包含一个backlog参数,表示连接请求队列(accept队列)的最大长度。当队列已满时,新的连接请求将被拒绝。backlog参数决定了服务器可以接受的同时连接请求数量。

TCP连接的建立过程如下:

  • SYN队列:在第一次握手期间,服务器将接收到的客户端连接请求(SYN数据包)放入SYN队列中。

  • accept队列:在第三次握手期间,一旦服务器发送完SYN和ACK数据包,等待客户端的ACK确认。当服务器收到客户端的确认后,它将客户端的连接从SYN队列中移入accept队列,此时连接已经建立,服务器可以与客户端进行数据传输。

 

UDP

 

 

image-20230810140723183

 

import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('localhost', 8888)
server_socket.bind(server_address)

while True:
    data, client_address = server_socket.recvfrom(1024)
    print(f"Received data from {client_address}: {data.decode('utf-8')}")
import socket

client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('localhost', 8888)

message = "Hello, server! This is a UDP message."
client_socket.sendto(message.encode('utf-8'), server_address)

 

TCP协议首部:

TCP首部包含了许多字段,用于控制和管理数据的传输,确保数据的可靠性和顺序性。

 

img

 

在这里插入图片描述

 

序列号和确认号:TCP 使用序列号来标识发送的数据字节,接收方使用确认号来确认已经接收到的字节。发送方维护一个发送窗口,指示可以发送的字节范围,接收方维护一个接收窗口,指示期望接收的字节范围。通过序列号和确认号,TCP 可以确保数据的正确顺序和接收情况。

超时重传:发送方会设置一个定时器,用于在一定时间内等待接收方的确认。如果定时器超时,发送方会重新发送未确认的数据。这个机制确保即使数据丢失,也能够通过重传达到接收方。

流量控制:TCP 使用滑动窗口机制来进行流量控制,确保发送方不会淹没接收方。接收方可以通过窗口大小来告知发送方它还能够接收多少数据,以避免数据拥塞。

拥塞控制:TCP 使用拥塞控制机制来避免网络拥塞。它通过动态调整发送速率,根据网络状况来控制数据的发送。如果网络出现拥塞,TCP 会减少发送速率以避免进一步加重拥塞。

确认机制:接收方会对接收到的数据发送确认,以便发送方知道数据已经被成功接收。如果发送方没有收到确认,会重传数据。同时,TCP 还实现了累积确认,即确认号表示已经成功接收到的最大连续字节。

通过以上的机制,TCP 可以在不可靠的网络环境中实现可靠的数据传输。尽管 TCP 的可靠性机制会引入一些额外的开销,但它确保了数据的正确性和完整性,适用于需要高可靠性的应用场景,如文件传输、网页浏览等。

 

UDP协议首部:

UDP首部相对简单,仅包含了几个基本字段。

源端口(Source Port)和目标端口(Destination Port): 16位字段,分别指示源和目标应用程序的端口号。

长度(Length): 16位字段,指示UDP首部和数据的总长度,以字节为单位。

校验和(Checksum): 16位字段,用于检测UDP首部和数据在传输过程中的错误。在UDP协议中,校验和是可选的,可以被设置为0表示不使用。

UDP协议相对于TCP协议更为简单,适用于不需要可靠性和顺序性保证的数据传输场景,如音频、视频流等。而TCP协议提供了更多的控制和管理机制,确保了数据的可靠传输和有序性。

 

HTTP/0.9:最早的版本,只支持 GET 方法,没有头部信息,仅用于获取 HTML 文档。

HTTP/1.0:引入了多种请求方法(GET、POST、HEAD)、状态码、头部信息,支持传输多种类型的数据,但每个请求都需要建立新的连接。

HTTP/1.1:引入了持久连接(keep-alive)、流水线(pipelining)、分块传输编码等特性,减少了连接建立的开销,提高了性能。也引入了缓存控制、虚拟主机支持等功能。

客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。 但是服务器必须按照接收请求的顺序发送对这些管道化请求的响应

HTTP/2:引入了多路复用(不需要考量附送顺序和接受顺序)、头部压缩、服务器推送(CSS,JS)、二进制等功能,进一步提升了性能和效率。

 

想象一下,您是一家繁忙的披萨店的店主,而店员是处理连接的线程。披萨店里有很多桌子,每个桌子都坐着不同的顾客,每位顾客都点了不同种类的披萨。您是店主,需要确保每位顾客都得到及时的服务。

现在,您可以有两种处理方式:

  1. 每桌一店员(传统多线程):您雇佣了很多店员,每个店员负责一张桌子上的顾客。这样可以确保每位顾客得到专门的服务,但随着顾客数量的增加,您需要雇佣更多的店员,管理变得复杂。

  2. 一个店员处理多桌(I/O 多路复用):您雇佣了一个聪明的店员,这个店员可以同时观察多张桌子上的顾客。他可以注意到哪些桌子上的顾客需要点菜,哪些桌子上的顾客已经吃完了。这个店员非常高效,可以在不浪费时间的情况下为每个桌子上的顾客提供服务。

在这个比喻中,每张桌子就代表一个连接,每位顾客就代表连接上的事件(如数据到达)。传统多线程就像为每个桌子雇佣一个店员,而 I/O 多路复用就像一个聪明的店员同时处理多张桌子上的顾客。通过使用一个线程进行多路复用,您可以高效地处理多个连接,避免了为每个连接都创建一个线程的开销。

 

中间件的执行顺序: 在ASP.NET Core中,每个HTTP请求都会经过一系列的中间件。中间件的执行顺序是按照它们被添加到应用程序中的顺序来决定的。请求首先进入第一个中间件,然后按照顺序依次通过其他中间件,最后进入处理终点(如控制器或静态文件处理器)。响应则会按照相反的顺序经过中间件,从处理终点返回到客户端。

中间件的结构: 中间件是一个C#类,它需要具备以下特点:

  • 具有一个接受 HttpContext 参数的构造函数。

  • 具有一个名为 InvokeInvokeAsync 的方法,用于处理请求和响应。

中间件的添加:Startup.cs文件中,可以使用app.UseMiddleware<T>方法将中间件添加到应用程序的请求处理管道中。你可以通过多次调用这个方法来添加多个中间件,它们将按照添加的顺序依次执行。

内置中间件: ASP.NET Core提供了一些内置的中间件,用于常见的任务,如路由、身份验证、静态文件服务等。你可以通过调用app.UseRoutingapp.UseAuthentication等方法来启用这些中间件。

自定义中间件: 你可以自己编写并添加自定义的中间件来满足应用程序特定的需求。自定义中间件可以用于执行各种任务,如性能监测、日志记录、请求修改等。

下面是一个简单的示例,展示如何创建一个简单的自定义中间件:

using System.Diagnostics;

namespace TEST0812
{
    public class TimingMiddleware
    {
        private readonly RequestDelegate _next;

        public TimingMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();

            await _next(context); // 调用下一个中间件或终点处理

            stopwatch.Stop();
            var elapsedMilliseconds = stopwatch.ElapsedMilliseconds;

            // 记录请求处理时间
            Console.WriteLine($"Request for {context.Request.Path} took {elapsedMilliseconds} ms");
        }
    }
}

// Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 添加自定义中间件到管道
    app.UseMiddleware<CustomMiddleware>();

    // ...
}
public class ErrorLoggingMiddleware
{
    private readonly RequestDelegate _next;

    public ErrorLoggingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context); // 调用下一个中间件或终点处理
        }
        catch (Exception ex)
        {
            LogError(ex);
            // 返回适当的错误响应,或者继续抛出异常以由其他中间件或终点处理进行处理
            throw;
        }
    }

    private void LogError(Exception ex)
    {
        // 将异常信息记录到日志文件
        string logMessage = $"{DateTime.UtcNow.ToString()} - Error: {ex.Message}\n";
        File.AppendAllText("error_log.txt", logMessage, Encoding.UTF8);
    }
}

 

权限管理

public async Task InvokeAsync(HttpContext context)
{
    string token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
    if (token != null)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_jwtService.SecretKey); // Replace with your key

        try
        {
            var claimsPrincipal = tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                // Validation parameters
            }, out SecurityToken validatedToken);

            // Extract user roles from claims or elsewhere
            var userRoles = GetUserRolesFromClaims(claimsPrincipal.Claims);

            // Create a new ClaimsIdentity with user roles
            var identity = new ClaimsIdentity(claimsPrincipal.Identity);
            foreach (var role in userRoles)
            {
                identity.AddClaim(new Claim(ClaimTypes.Role, role));
            }

            // Create a new ClaimsPrincipal with the updated identity
            var newClaimsPrincipal = new ClaimsPrincipal(identity);
            context.User = newClaimsPrincipal;
        }
        catch (Exception)
        {
            context.Response.Redirect("/Error");
        }
    }

    await _next(context);
}

中间件是先于过滤器执行的

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class AdminController : ControllerBase
{
    [HttpGet]
    [Authorize(Roles = "Admin, SuperAdmin")] // Only allow Admin and SuperAdmin roles
    public IActionResult GetAdminData()
    {
        // Handle admin data retrieval logic
        return Ok("Admin data");
    }
}

 

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Apply the authentication middleware to all routes except the login route
    app.Map("/login", loginApp =>
    {
        var username = context.Request.Form["username"];
        var password = context.Request.Form["password"];
        // Validate user credentials (this is just an example)
        if (IsValidUser(username, password))
        {
            // Generate JWT token
            var token = GenerateJwtToken(username); // Your token generation logic here
            context.Response.Headers.Add("Authorization", $"Bearer {token}");
            
            // Return success response
            context.Response.StatusCode = 200;
            await context.Response.WriteAsync("Login successful");
        }
        else
        {
            // Return failure response
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Invalid credentials");
        }
    });

    // Use the authentication and authorization middleware for all other routes
    app.UseAuthentication();
    app.UseAuthorization();

    // Other middleware and routing configuration
    // ...
}

 

  • AddScoped:每个HTTP请求创建一个服务实例,请求内共享相同的实例。

  • AddTransient:每次请求创建一个新的服务实例。

  • AddSingleton:整个应用程序生命周期中只创建一个服务实例,所有请求共享相同的实例。

services.AddScoped<IMyService, MyService>(); // 使用接口和实现类注册
services.AddScoped<IMyService>(new MyService()); // 使用工厂方法注册,省略了 provider =>

services.AddScoped<IMyService, MyService>(); // 使用接口和实现类注册
services.AddScoped<IMyService>(new MyService()); // 使用工厂方法注册,省略了 provider =>

 

RESTful API是一种遵循REST原则的API设计风格。REST(Representational State Transfer)是一种基于HTTP协议的架构风格,它强调资源的抽象性和状态的转移。RESTful API允许客户端通过HTTP请求来执行各种操作,如获取资源、创建、更新和删除资源等。

RESTful API的关键特点包括:

  1. 无状态性(Statelessness): 每个请求都应该包含足够的信息,以便服务器可以理解请求,而不需要依赖之前的请求或状态。

  2. 资源(Resources): API的核心是资源,每个资源都有一个唯一的标识符(URL),客户端可以通过HTTP方法对资源进行操作。

  3. 统一接口(Uniform Interface): RESTful API应该使用统一的方法(如GET、POST、PUT、DELETE)和标准的HTTP状态码来进行操作和传达状态。

  4. 自描述性(Self-descriptive): API的请求和响应应该是自描述的,包含足够的信息,使开发人员能够理解如何使用API。

  5. 客户端-服务器分离(Client-Server Separation): 客户端和服务器应该独立地演化,使得客户端和服务器可以分别进行优化。

  6. 资源命名: 使用清晰的、有意义的资源命名,将资源表示为URL路径的一部分。

  7. 使用HTTP方法: 使用合适的HTTP方法来执行不同的操作,如GET(获取)、POST(创建)、PUT(更新)、DELETE(删除)等。

下面是一个简单的例子来说明拓展方法的使用:

using System;

namespace ExtensionMethodsExample
{
  // 定义一个静态类来存放拓展方法
  public static class StringExtensions
  {
      // 定义一个拓展方法,用于将字符串反转
      public static string Reverse(this string input)
      {
          char[] chars = input.ToCharArray();
          Array.Reverse(chars);
          return new string(chars);
      }
  }

  class Program
  {
      static void Main(string[] args)
      {
          string original = "Hello, World!";
          string reversed = original.Reverse(); // 调用拓展方法
          Console.WriteLine(reversed); // 输出:!dlroW ,olleH
      }
  }
}

在这个例子中,Reverse方法是一个拓展方法,它扩展了string类型。通过在string类型上调用Reverse方法,我们能够实现字符串的反转,即使string类本身没有提供这个方法。