在使用 ASP.NET Core 构建基于角色的 Web API 时,代码优先方法是一种强大而高效的方法。使用它,我们可以在代码中定义数据模型和关系,然后自动生成相应的数据库模式。这会带来什么?当然是更快的开发周期和更大的灵活性。为什么?因为可以快速轻松地更改数据模型,而无需直接修改数据库模式。 您可以在 swagger.io 上阅读有关设计优先和代码优先方法的更多信息。

在本教程中,我们将介绍使用 ASP.NET Core 6 创建基于角色的 Web API 的步骤。我们将使用 Swagger UI 来可视化并与我们的端点以及作为数据库的 MS SQL Server 进行交互。该应用程序将包含一个身份验证模块和一个事件模块。登录用户将能够查看与其帐户关联的事件,而具有管理员角色的用户可以创建、更新和删除事件。

示例下载:https://download.csdn.net/download/hefeng_aspnet/91490040  

让我们开始吧!

项目设置

首先,我们需要设置项目。打开 Visual Studio,创建一个新项目,然后选择 ASP.NET Core Web API。

选择应用程序的名称,然后单击“下一步”。

设置 API 数据库

初始化应用程序后,我们需要配置数据库。我们将使用 EntityFrameworkCore 作为 ORM  ,以便它帮助我们管理数据库。因此,我们需要安装一些软件包。

成功安装软件包后,下一步要做的是创建 DbContext。创建 DataContext.cs 文件并继承 DBContext 类。在这里我们将定义表。

public class DataContext: DbContext
{
  public DataContext(DbContextOptions options): base(options)
  {
  }

  //Define our tables
}

然后,我们打开 Program.cs 文件并添加 dbContext。我们需要指定  来自 appsettings.json 文件的 dbProvider和连接字符串。 dbProvider 可以是 SqlServer、MySql 或 InMemory。

// Add Db context

var dbProvider = builder.Configuration.GetConnectionString("Provider");

builder.Services.AddDbContext < DataContext > (options =>
  {

    if (dbProvider == "SqlServer")
    {
      options.UseSqlServer(builder.Configuration.GetConnectionString("SqlServerConnectionString"));
    }
  });

确保已  在 appsettings.json文件中添加了ConnectionString 和 Provider  ,如下所示:


"ConnectionStrings": {
  "Provider": "SqlServer",
  "SqlServerConnectionString": "Data Source=(localdb)\\MSSQLLocalDB;Database=HRApplication2;Integrated Security=True;Connect Timeout=30; "
},

配置 DbContext后,需要生成数据库模型。在本例中,我们需要两个实体(User 和 Event)以及第三个表(UserEvent)来建立它们之间的多对多关系。为此,建议创建一个 Models 文件夹,并在其中创建一个 DbModels 子文件夹,用于创建数据库实体。

让我们从 用户 模型开始。每个用户都应该有一个唯一的 ID、电子邮件、名字、姓氏、密码(将以哈希格式存储)、角色(在演示中可以是用户或管理员)以及 与UserEvent表 关联的 UserEvents  。

public class User
{
    public string UserId { get; set; }
    public string Email { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Password { get; set; }
    public string Role { get; set; }
    public IList <UserEvent> UserEvents { get; set; }
}

事件模型还应具有唯一的 ID、标题、类别、日期以及与 UserEvents 表的关系。

public class Event
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string Category { get; set; }
    public DateTime Date { get; set; }
    public IList <UserEvent> UserEvents { get; set; }
}

由于一个用户可以参加多个活动,并且一个活动也可以由多个用户参加,因此我们需要在这些实体之间建立多对多关系。为此,我们将创建一个名为 UserEvents的附加表。该表将包含 UserId 和 EventId列,用于建立用户实体 和 事件实体 之间的关系  。

public class UserEvent
{
    public string UserId { get; set; }
    public User User { get; set; }
    public string EventId { get; set; }
    public Event Event { get; set; }
}

创建数据库模型后,下一步就是在 DbContext中注册它们。为此,我们可以导航到 DataContext.cs 文件,将所有实体添加为 DbSets,并声明关系和主键。这可以通过重写 OnModelCreating 方法并使用 Fluent API 配置关系和键来实现。完成后,结果应如下所示:

public class DataContext: DbContext
{
  public DataContext(DbContextOptions options): base(options)
  {
  }
  
  public DbSet <User> Users { get; set; }
  public DbSet < Event > Events { get; set; }
  public DbSet < UserEvent > UserEvents { get; set; }

  protected override void OnModelCreating(ModelBuilder builder)
  {
    base.OnModelCreating(builder);

    builder.Entity<User>()
      .HasKey(u => new {
        u.UserId
      });

    builder.Entity<Event>()
      .HasKey(e => new {
        e.Id
      });

    builder.Entity<UserEvent>()
      .HasKey(ue => new {
        ue.UserId, ue.EventId
      });

    builder.Entity<UserEvent>()
      .HasOne(ue => ue.User)
      .WithMany(user => user.UserEvents)
      .HasForeignKey(u => u.UserId);

    builder.Entity<UserEvent>()
      .HasOne(uc => uc.Event)
      .WithMany(ev => ev.UserEvents)
      .HasForeignKey(ev => ev.EventId);
  }
}

在我们准备好数据库设计之后,我们应该生成一个将创建数据库的初始迁移。

打开包管理器控制台 并写入命令:

添加迁移初始创建

成功执行后,我们应该使用以下命令更新数据库:

更新数据库

然后使用 Microsoft SQL Management Studio,您应该会看到新创建的数据库。

配置 AutoMapper

AutoMapper 会帮助我们将一个模型转换为另一个模型。这会将输入模型转换为 dbModel。 我们这样做的原因是,我们可能不需要将一个模型的所有属性都包含在另一个模型中。您将在本教程的后面部分看到我们如何使用 AutoMapper。在此之前,我们需要先配置它。 您可以在官方文档中找到更多关于 AutoMapper 的详细说明。 

首先,我们必须安装 AutoMaper NuGet 包。之后,我们可以生成一个 MappingProfiles.cs 文件来定义所有映射。为了便于组织,建议将此文件创建在 Helpers 文件夹下。

为了声明映射,  MappingProfiles 应该继承 Profile 类,并且我们可以使用 CreateMap<from, to>() 方法声明映射。如果我们需要反向映射模型,可以添加 .ReverseMap() 方法。

一旦我们完成映射,我们必须导航到 Program.cs 文件并  使用我们的 MappingProfiles注册AutoMapper

…
var config = new MapperConfiguration(cfg =>{cfg.AddProfile(new MappingProfiles());});var mapper = config.CreateMapper();builder.Services.AddSingleton(mapper);
… 

设置身份验证

我们将使用 JWT 令牌进行身份验证。它们为我们提供了一种以 JSON 对象形式在各方之间安全地传输信息的方法。您可以 在此处阅读有关 JWT 令牌的更多信息。要使用它们,我们必须首先安装必要的 NuGet 包。我们需要 Microsoft.IdentityModel.Tokens 和 Microsoft.AspNetCore.Authentication.JwtBearer

接下来,我们必须在appsettings.json 文件中定义一些 token 配置 。这些配置包括 Issuer、Audience 和 SecretKey。

"Jwt": {"Issuer": "https://localhost:7244/","Audience": "https://localhost:7244/","Key": "S1u*p7e_r+S2e/c4r6e7t*0K/e7y"
}

定义令牌配置后,我们可以在 Program.cs 文件中配置 JWT 服务。这涉及指定将使用的架构以及任何必要的验证参数。 

…
builder.Services.AddAuthentication(options =>{options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(o =>{o.TokenValidationParameters = new TokenValidationParameters{ValidIssuer = builder.Configuration["Jwt:Issuer"],ValidAudience = builder.Configuration["Jwt:Audience"],IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),ValidateIssuer = true,ValidateAudience = true,ValidateLifetime = false,ValidateIssuerSigningKey = true};});
…

确保您还添加了 app.UseAuthentication();

设置Swagger

为了使用 Swagger UI测试我们的应用程序端点,我们必须  在 Program.cs文件中包含app.UseSwaggerUI()  。

之后,我们必须生成一个 AuthResponse 过滤器,以便使用 JWT 令牌测试已验证的端点。为此,我们可以创建一个  实现 IOperationFilter接口的AuthResponsesOperationFilter类 。Apply 方法应该包含将 AuthResponse 过滤器添加到 Swagger 所需的逻辑。

public class AuthResponsesOperationFilter: IOperationFilter
{public void Apply(OpenApiOperation operation, OperationFilterContext context){var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true).Union(context.MethodInfo.GetCustomAttributes(true)).OfType<AuthorizeAttribute>();if (authAttributes.Any()){var securityRequirement = new OpenApiSecurityRequirement(){{new OpenApiSecurityScheme{Reference = new OpenApiReference{Type = ReferenceType.SecurityScheme,Id = "Bearer"}},new List<string>()}};operation.Security = new List<OpenApiSecurityRequirement> {securityRequirement};operation.Responses.Add("401", new OpenApiResponse {Description = "Unauthorized"});}}
}

之后,确保已在 Program.cs  .AddSwaggerGen 方法中将过滤器添加为选项。 

builder.Services.AddSwaggerGen(option =>{option.SwaggerDoc("v1", new OpenApiInfo {Title = "Northwind CRUD", Version = "v1"});option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme{In = ParameterLocation.Header,Description = "Please enter a valid token",Name = "Authorization",Type = SecuritySchemeType.Http,BearerFormat = "JWT",Scheme = "bearer"});option.OperationFilter < AuthResponsesOperationFilter > ();});

您可以在官方文档中阅读“什么是 Swagger?”的更详细解释 。

注册端点

完成配置后,我们可以继续创建注册端点。第一步是生成一个 RegisterInputModel.cs 文件,该文件位于 Models/InputModels 文件夹下。

注册过程需要 Email、FirstName、LastName、Password 和 ConfirmedPassword 字段。所有这些字段都是必填项,因此我们将添加 [Required] 属性。此外,我们还将为 Email 字段添加 [EmailAddress] 属性。我们可以根据需要添加其他属性,例如最小长度和最大长度。但出于演示目的,我们将仅使用这些属性。

public class RegisterInputModel
{[EmailAddress]public string Email { get; set; }[Required]public string FirstName { get; set; }[Required]public string LastName { get; set; }[Required]public string Password { get; set; }[Required]public string ConfirmedPassword { get; set; }
}

接下来,我们必须向MappingProfiles.cs文件添加一个映射 ,以便在RegisterInputModel 和 User模型 之间实现  双向转换。

创建映射<RegisterInputModel,用户>().ReverseMap();

为了保持关注点分离,我们将创建一个 Services 文件夹。每个模块都有自己的服务用于与数据库交互。我们可以先生成一个 AuthService.cs 文件,并注入 DataContext 和 Configuration

AuthService.cs中的第一个方法  应该是 GenerateJwtToken,它将电子邮件和角色作为参数并返回包含用户信息的 JWT 令牌。

public string GenerateJwtToken(string email, string role)
{var issuer = this.configuration["Jwt:Issuer"];var audience = this.configuration["Jwt:Audience"];var key = Encoding.ASCII.GetBytes(this.configuration["Jwt:Key"]);var tokenDescriptor = new SecurityTokenDescriptor{Subject = new ClaimsIdentity(new []{new Claim("Id", Guid.NewGuid().ToString()),new Claim(JwtRegisteredClaimNames.Sub, email),new Claim(JwtRegisteredClaimNames.Email, email),new Claim(ClaimTypes.Role, role),new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())}),Expires = DateTime.UtcNow.AddMinutes(5),Issuer = issuer,Audience = audience,SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)};var tokenHandler = new JwtSecurityTokenHandler();var token = tokenHandler.CreateToken(tokenDescriptor);return tokenHandler.WriteToken(token);
}

为了对密码进行哈希处理,我们将使用 BCrypt.Net.BCrypt。首先,我们必须安装该包并将其作为 using 语句添加到文件开头。

using BC = BCrypt.Net.BCrypt;

之后,我们将创建几个辅助方法。其中一个将验证具有给定电子邮件的用户是否存在,另一个将对用户进行身份验证,另外两个将通过电子邮件和 ID 获取用户。

public bool IsAuthenticated(string email, string password)
{var user = this.GetByEmail(email);return this.DoesUserExists(email) && BC.Verify(password, user.Password);
}public bool DoesUserExists(string email)
{var user = this.dataContext.Users.FirstOrDefault(x => x.Email == email);return user != null;
}public User GetById(string id)
{return this.dataContext.Users.FirstOrDefault(c => c.UserId == id);
}public User GetByEmail(string email)
{return this.dataContext.Users.FirstOrDefault(c => c.Email == email);
}

在创建register方法之前,我们必须先创建一个方法来生成唯一的ID。该方法可以定义如下: 

public class IdGenerator
{public static string CreateLetterId(int length){var random = new Random();const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());}
}

现在我们可以继续实现 Register 方法了。在这个方法中,我们将生成一个唯一的 ID,检查它是否已经存在,如果存在,则生成一个新的。然后,我们将对用户密码进行哈希处理,并将新用户添加到数据库中。 

public User RegisterUser(User model)
{var id = IdGenerator.CreateLetterId(10);var existWithId = this.GetById(id);while (existWithId != null){id = IdGenerator.CreateLetterId(10);existWithId = this.GetById(id);}model.UserId = id;model.Password = BC.HashPassword(model.Password);var userEntity = this.dataContext.Users.Add(model);this.dataContext.SaveChanges();return userEntity.Entity;
}

如果您尚未创建 AuthController  ,现在是时候了。转到 Controllers 文件夹并添加 AuthController  ,它应该继承自 Controller 类。我们还应该添加 [ApiController] 属性和 [Route( “[controller]” )]  ,以便 Swagger 识别它。

之后,我们应该注入 mapper、authService 和 logger(如果使用的话),并创建 RegisterMethod。它应该只允许未经身份验证的用户通过 post 请求访问。它应该接受 RegisterInputModel 作为参数,并检查 ModelState是否 有效。如果有效,它将为该用户生成 JWT 令牌。整个方法应该如下所示:

[AllowAnonymous]
[HttpPost("Register")]
public ActionResult<string> Register(RegisterInputModel userModel)
{try{if (ModelState.IsValid){if (userModel.Password != userModel.ConfirmedPassword){return BadRequest("Passwords does not match!");}if (this.authService.DoesUserExists(userModel.Email)){return BadRequest("User already exists!");}var mappedModel = this.mapper.Map<RegisterInputModel, User>(userModel);mappedModel.Role = "User";var user = this.authService.RegisterUser(mappedModel);if (user != null){var token = this.authService.GenerateJwtToken(user.Email, mappedModel.Role);return Ok(token);}return BadRequest("Email or password are not correct!");}return BadRequest(ModelState);} catch (Exception error){logger.LogError(error.Message);return StatusCode(500);}
}

登录功能

登录功能类似,只是我们需要在数据库中搜索用户。首先,我们应该创建 LoginInputModel.cs 文件,该文件只包含 Email 和 Password 字段。别忘了在 MappingProfiles.cs文件中添加该文件 ,否则将无法正常工作。 

public class LoginInputModel
{[EmailAddress][Required]public string Email { get; set; }[Required]public string Password { get; set; }
}

然后在 AuthController.cs中 创建 Login 方法,该方法接受 LoginInputModel 作为参数,并检查用户是否已通过身份验证。如果已通过身份验证,则生成一个 token。否则,返回错误。 

[AllowAnonymous]
[HttpPost("Login")]
public ActionResult <string> Login(LoginInputModel userModel)
{try{if (ModelState.IsValid){if (this.authService.IsAuthenticated(userModel.Email, userModel.Password)){var user = this.authService.GetByEmail(userModel.Email);var token = this.authService.GenerateJwtToken(userModel.Email, user.Role);return Ok(token);}return BadRequest("Email or password are not correct!");}return BadRequest(ModelState);} catch (Exception error){logger.LogError(error.Message);return StatusCode(500);}
}

为事件添加 CRUD

完成身份验证后,我们可以开发事件的端点。我们将创建完整的 CRUD 操作。与用户操作类似,我们应该创建 EventService.cs 文件。该文件将包含以下方法:通过 ID 获取事件、获取特定用户的所有事件、创建新事件、更新现有事件以及删除事件。整个文件应如下所示:

public class EventService
{private readonly DataContext dataContext;public EventService(DataContext dataContext){this.dataContext = dataContext;}public Event[] GetAllForUser(string email){var user = this.dataContext.Users.FirstOrDefault(user => user.Email == email);return this.dataContext.Events.Include(ev => ev.UserEvents).Where(e => e.UserEvents.FirstOrDefault(ue => ue.UserId == user.UserId) != null).ToArray();}public Event GetById(string id){return this.dataContext.Events.Include(ev => ev.UserEvents).FirstOrDefault(c => c.Id == id);}public Event Create(Event model){var id = IdGenerator.CreateLetterId(6);var existWithId = this.GetById(id);while (existWithId != null){id = IdGenerator.CreateLetterId(6);existWithId = this.GetById(id);}model.Id = id;var eventEntity = this.dataContext.Events.Add(model);this.dataContext.SaveChanges();return eventEntity.Entity;}public Event Update(Event model){var eventEntity = this.dataContext.Events.Include(ev => ev.UserEvents).FirstOrDefault(c => c.Id == model.Id);if (eventEntity != null){eventEntity.Title = model.Title != null ? model.Title : eventEntity.Title;eventEntity.Date = model.Date != null ? model.Date : eventEntity.Date;eventEntity.Category = model.Category != null ? model.Category : eventEntity.Category;eventEntity.UserEvents = model.UserEvents.Count! > 0 ? model.UserEvents : eventEntity.UserEvents;this.dataContext.SaveChanges();}return eventEntity;}public Event Delete(string id){var eventEntity = this.GetById(id);if (eventEntity != null){this.dataContext.Events.Remove(eventEntity);this.dataContext.SaveChanges();}return eventEntity;}
}

接下来,您将需要转到控制器并为每个请求设置一个方法。

我们将创建一个 EventBindingModel  ,用于存储来自事件模型的所有必要数据。

对于 GetAll 方法,确保它使用 GET 请求并检索用户的令牌、对其进行解码并获取该用户的事件。

[HttpGet]
[Authorize]
public ActionResult<EventBindingModel[]> GetAll()
{try{var userEmail = this.authService.DecodeEmailFromToken(this.Request.Headers["Authorization"]);var events = this.eventService.GetAllForUser(userEmail);return Ok(this.mapper.Map<Event[], EventBindingModel[]> (events));} catch (Exception error){logger.LogError(error.Message);return StatusCode(500);}
}…
public string DecodeEmailFromToken(string token)
{var decodedToken = new JwtSecurityTokenHandler();var indexOfTokenValue = 7;var t = decodedToken.ReadJwtToken(token.Substring(indexOfTokenValue));return t.Payload.FirstOrDefault(x => x.Key == "email").Value.ToString();
}
…

通过id获取也应该是以  id作为参数的GET请求。 

[HttpGet("{id}")]
[Authorize]
public ActionResult<Event> GetById(string id)
{try{var eventEntity = this.eventService.GetById(id);if (eventEntity != null){return Ok(eventEntity);}return NotFound();} catch (Exception error){logger.LogError(error.Message);return StatusCode(500);}
}

删除端点将是 DELETE 请求,并且将以 id 作为参数。 

[HttpDelete("{id}")]
[Authorize(Roles = "Administrator")]
public ActionResult<Event> Delete(string id)
{try{var eventEntity = this.eventService.Delete(id);if (eventEntity != null){return Ok(eventEntity);}return NotFound();} catch (Exception error){logger.LogError(error.Message);return StatusCode(500);}
}

为了简化添加或更新事件记录的流程,我们创建一个 专门用于创建和更新操作的EventInputModel  。此模型仅需要我们提供用户事件的必要属性,包括标题、类别、日期、用户 ID 和事件 ID。使用此模型,我们无需为每个操作指定 事件 模型的所有属性。 

[HttpPost]
public ActionResult<Event> Create(EventInputModel model)
{try{if (ModelState.IsValid){var mappedModel = this.mapper.Map < EventInputModel,Event > (model);var eventEntity = this.eventService.Create(mappedModel);return Ok(eventEntity);}return BadRequest(ModelState);} catch (Exception error){logger.LogError(error.Message);return StatusCode(500);}
}

更新将是 PUT 请求,并且还将以 EventInputModel 作为参数。 

[HttpPut]
public ActionResult<Event> Update(EventInputModel model)
{try{if (ModelState.IsValid){var mappedModel = this.mapper.Map<EventInputModel, Event>(model);var eventEntity = this.eventService.Update(mappedModel);if (eventEntity != null){return Ok(eventEntity);}return NotFound();}return BadRequest(ModelState);} catch (Exception error){logger.LogError(error.Message);return StatusCode(500);}
}

添加基于角色的授权

为了将某些操作限制为特定用户角色,我们可以使用基于角色的授权。例如,在我们的场景中,我们希望将事件的创建、更新和删除端点的访问权限限制为具有管理员角色的用户。

要进行设置,我们需要 在Program.cs文件 中 添加app.UseAuthorization();  。然后,对于每个需要限制访问的端点,我们将添加 [Authorize] 属性,该属性将指定允许的角色。例如,我们可以确保只有管理员才能访问 Delete 端点。

…
[Authorize(Roles = "Administrator")]
public ActionResult<Event> Delete(string id)
…

创建 DbSeeder

在运行应用程序时,我们经常希望在数据库中预先填充一些数据,无论是出于测试目的还是其他原因。这时就需要使用种子功能了。首先,我们需要定义要使用的数据。

为此,我们可以创建一个 Resources 文件夹并添加两个 JSON 文件:一个用于用户,一个用于事件。这些文件应包含我们想要填充到数据库中的数据。例如,我们的文件可能如下所示:

[{"UserId": "USERABCDE","FirstName": "Kate","LastName": "Lorenz","Password": "kate.lorenz","Email": "klorenz@hrcorp.com","Role": "Administrator"},{"UserId": "ASERABCDE","FirstName": "Anthony","LastName": "Murray","Password": "anthony.murray","Email": "amurray@hrcorp.com","Role": "User"}
]

接下来,我们应该创建一个 包含 Seed 方法的DbSeeder 类。此方法将读取我们之前定义的数据并将其填充到数据库中。为此,我们需要将 dbContext 作为参数传入。 

public class DBSeeder
{public static void Seed(DataContext dbContext){ArgumentNullException.ThrowIfNull(dbContext, nameof(dbContext));dbContext.Database.EnsureCreated();var executionStrategy = dbContext.Database.CreateExecutionStrategy();executionStrategy.Execute(() => {using(var transaction = dbContext.Database.BeginTransaction()){try{// Seed Usersif (!dbContext.Users.Any()){var usersData = File.ReadAllText("./Resources/users.json");var parsedUsers = JsonConvert.DeserializeObject <User[]>(usersData);foreach(var user in parsedUsers){user.Password = BC.HashPassword(user.Password);}dbContext.Users.AddRange(parsedUsers);dbContext.SaveChanges();}// Seed Eventsif (!dbContext.Events.Any()){var eventsData = File.ReadAllText("./Resources/events.json");var parsedEvents = JsonConvert.DeserializeObject <Event[]>(eventsData);dbContext.Events.AddRange(parsedEvents);dbContext.SaveChanges();}transaction.Commit();} catch (Exception ex){transaction.Rollback();}}});}
}

之后,在我们的 Helpers 文件夹中,我们应该创建一个数据库初始化程序扩展,它将运行 Seed 方法。 

public static class DBInitializerExtension
{public static IApplicationBuilder UseSeedDB(this IApplicationBuilder app){ArgumentNullException.ThrowIfNull(app, nameof(app));using var scope = app.ApplicationServices.CreateScope();var services = scope.ServiceProvider;var context = services.GetRequiredService < DataContext > ();DBSeeder.Seed(context);return app;}
} 

最后,我们需要打开 Program.cs 文件并添加 app.UseSeedDB() 方法。这确保我们的应用程序启动时会检查数据库中是否有数据。如果没有,我们之前创建的 Seed 方法将自动使用我们定义的数据填充它。 

添加 CORS

为了为我们的端点启用跨域资源共享(CORS),我们需要在 Program.cs 文件中添加一个 cors 服务。在这种情况下,我们将允许从任何区域访问,但如果您愿意,也可以指定特定的域。

builder.Services.AddCors(policyBuilder =>policyBuilder.AddDefaultPolicy(policy =>policy.WithOrigins("*").AllowAnyHeader().AllowAnyHeader())
);

然后添加 app.UseCors(); 方法。

这将允许我们从前端应用程序访问我们的 API。

您可以在此处阅读有关跨域资源共享(CORS)的更多信息。

总结一下……

在构建安全且可扩展的 Web 应用程序时,使用 ASP .NET Core 创建基于角色的 API 至关重要。通过使用基于角色的授权,您可以根据分配给用户的角色来控制对 API 资源的访问。这确保只有授权用户才能访问敏感数据或执行关键操作,从而使您的应用程序更加安全可靠。在本教程中,我们学习了如何从零到一创建一个简单的基于角色的 API。

示例下载:https://download.csdn.net/download/hefeng_aspnet/91490040 

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/95500.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/95500.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/95500.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无字母数字命令执行

写在前面 说白了数字还是好构造的&#xff0c;bash的算数拓展&#xff01; base64命令 这玩意说白了有点鸡肋&#xff0c;因为你得知道flag的文件名和位置&#xff01; base64 flag.php这个会将flag.php里面的内容给base64编码输出来。那么如何用无字母数字构造呢&#xff1f; …

AAB包转apks转apk

1. 下载bundletool-all-1.17.2.jar&#xff08;不一定非得1.17.2&#xff0c;可以其他版本&#xff09; https://github.com/google/bundletool/releases/tag/1.17.2 2. 在aab、keystore、bundletool-all-1.17.2.jar的目录下&#xff0c;运行指令 java -jar bundletool-all-1…

从零开始:用uv构建并发布一个Python CLI应用,集成CI/CD自动化发布与Docker容器化部署

使用uv构建并发布一个完整的Python CLI应用 概述 初始化项目 编写应用代码 定义项目 (`pyproject.toml`) 使用`uv`安装依赖 本地运行和测试 依赖锁定 构建 发布 生产环境实践之CI/CD 创建工作流配置文件 配置GitHub Secrets 创建和推送tag 验证发布 生产环境实践之Docker 创建D…

如何在Qt中使用周立功USB转CAN卡

如何在 Qt 中使用周立功 USB 转 CAN 卡 文章目录如何在 Qt 中使用周立功 USB 转 CAN 卡一、简介二、准备工作三、使用四、运行效果五、写在最后​一、简介 最近在工程中用到了周立功的 USB 转 CAN 卡&#xff0c;需求是要通过上位机进行通信&#xff0c;因此有了这篇文章。 有…

JavaScript 源码剖析:从字节码到执行的奇妙旅程

JavaScript&#xff0c;这门风靡全球的脚本语言&#xff0c;以其灵活性和跨平台性征服了无数开发者。我们每天都在使用它&#xff0c;但它在后台是如何工作的&#xff1f;一段看似简单的JS代码&#xff0c;在执行之前究竟经历了哪些“变形记”&#xff1f;今天&#xff0c;让我…

FPGA—硬件电路一旦上电配置完成,各个功能模块会并行地持续工作

1.示例代码参考这段代码是用 Verilog 编写的一个 LED 闪烁控制模块&#xff0c;主要实现了 LED 按一定时间间隔循环移位闪烁的功能。下面详细解释其架构组成&#xff1a;模块定义与端口声明模块名为 led_flash&#xff0c;包含三个端口&#xff1a;sys_clk&#xff1a;输入端口…

从零到上线:Docker、Docker Compose 与 Runtime 安装部署全指南(含实战示例与应用场景)

文章目录一、Docker 安装1. Ubuntu / Debian&#xff08;官方仓库&#xff09;2. RHEL / CentOS / Rocky / AlmaLinux3. 验证4. macOS / Windows&#xff08;Docker Desktop&#xff09;二、Docker Compose&#xff08;V2&#xff09;安装与基本用法1) 验证2) 最小示例&#xf…

Java基础篇02:基本语法

1 注释 注释是写在程序中对代码进行解释说明的文字&#xff0c;方便自己和其他人查看&#xff0c;以便理解程序的。注释分为三种&#xff1a;单行注释、多行注释、文档注释注释不影响代码的执行&#xff1a; 原因是编译后的文件已经没有注释了// 这是单行注释&#xff1a;。通常…

【SECS/GEM 】SECS/GEM 日志管理相关的消息

明白 ✅ 在 SECS/GEM 架构里&#xff0c;设备日志&#xff08;Equipment Logging 主要涉及 事件日志&#xff08;Event Log&#xff09;、报警日志&#xff08;Alarm Log&#xff09;、配方操作日志&#xff08;Recipe Log&#xff09;、以及用户操作/命令日志。这些日志通过 S…

ragas 框架使用Chat-GLM模型报API 调用参数有误,请检查文档

ragas 框架使用Chat-GLM模型报API 调用参数有误&#xff0c;请检查文档解决方案 from ragas.llms import LangchainLLMWrapper # 点击LangchainLLMWrapper 进入这个类找到这个方法直接 return 0.1出现问题原因 ChatGLM 不支持设置temperature等于0&#xff0c;默认的值太小了

Kaggle - LLM Science Exam 大模型做科学选择题

Kaggle - LLM Science Exam Science Exam Simple Approach w/ Model Hub | Kaggle Platypus2-70B with Wikipedia RAG | Kaggle 5个选项只有一个选项正确&#xff0c;目标&#xff1a;回答一个选项序列&#xff08;只有前三个有效&#xff09; 输出正确选项 &#xff08;可…

贪吃蛇鱼小游戏抖音快手微信小程序看广告流量主开源

核心优势&#xff1a;为流量主运营者与新手量身打造 1. 为流量主运营者破解成本困局 本地化运行&#xff0c;零服务器成本&#xff1a;数据运行与存储全程在用户手机本地完成&#xff0c;无需部署服务器及后台系统&#xff0c;彻底摆脱服务器租赁、维护等硬性支出&#xff0c;…

PDF Reader 编辑阅读工具(Mac中文)

原文地址&#xff1a;PDF Reader 编辑阅读 for Mac v5.2.0 PDF Reader Pro Mac&#xff0c;是一款PDF编辑阅读&#xff0c;PDF Reader Pro让您直接在 Mac 上进行PDF文件阅读、笔记、编辑、转换、创建PDF、签署PDFs、填写PDF Forms表单、设置密码、合并拆分文件、水印等等&…

Django REST framework:SimpleRouter 使用指南

1. SimpleRouter 是什么&#xff1f; SimpleRouter 是 DRF&#xff08;Django REST framework&#xff09;提供的路由器&#xff0c;能根据 ViewSet 自动生成标准的 REST 路由&#xff0c;包括&#xff1a; GET /resources/ → 列表&#xff08;list&#xff09;POST /resource…

覆盖Transformer、GAN:掩码重建正在重塑时间序列领域!

随着大数据与深度学习的发展&#xff0c;时间序列分析的建模能力显著提升&#xff0c;而掩码重建作为一种自监督学习范式&#xff0c;已成为提升序列表征能力的重要技术。该方法通过随机掩码部分数据并重建原始序列&#xff0c;迫使模型挖掘时序依赖性与潜在模式&#xff0c;在…

用AI做TikTok影视解说,全流程全自动成片,不懂外语也能做全球矩阵!

多语种解说&#xff1a; 短剧出海狂吸美金 多语种解说抢先机 TikTok、YouTube等平台&#xff0c;尤其在非英语市场&#xff0c;内容供给仍远远不足&#xff0c;每一个小语种市场都是潜在蓝海。 有人用英语讲仙侠、西语讲爽剧、日语讲宫斗、阿语讲悬疑&#xff0c;一夜涨粉百…

解密大语言模型推理:输入处理背后的数学与工程实践

解密大语言模型推理&#xff1a;输入处理背后的数学与工程实践当你向ChatGPT提问时&#xff0c;短短几秒内就能获得流畅的回答&#xff0c;这背后隐藏着怎样的技术魔法&#xff1f;答案在于大语言模型高效推理过程中精妙的输入处理机制。在现代大语言模型推理中&#xff0c;输入…

02、连接服务器的几种方式

02、连接服务器的几种方式 1、Xshell 适用于Windows https://www.xshell.com/en/free-for-home-school/ 2、Termius 适用于MacOS 直接苹果商店下载即可 3、IDEA 连接 Tools - Deployment - Browse Remote Host 1、打开Browse Remote Host2、添加服务3、输入服务器连接信息并测试…

高并发系统设计方案(直播场景)

最近在准备面试&#xff0c;正把平时积累的笔记、项目中遇到的问题与解决方案、对核心原理的理解&#xff0c;以及高频业务场景的应对策略系统梳理一遍&#xff0c;既能加深记忆&#xff0c;也能让知识体系更扎实&#xff0c;供大家参考&#xff0c;欢迎讨论。 1. 微服务拆分 …

网络编程基础:一文搞懂 Socket、HTTP、HTTPS、TCP/IP、SSL 的关系

在日常开发中&#xff0c;我们经常听到 Socket、HTTP、HTTPS、TCP/IP、SSL 这些术语&#xff0c;这些概念往往容易混淆&#xff0c;且让人感到困惑。本文将用最通俗易懂的方式来讲清这些网络概念及其相互关系。一、从寄信说起&#xff1a;网络通信的本质假如你要给远方的朋友寄…