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

友情链接英文翻译班级优化大师app

友情链接英文翻译,班级优化大师app,合肥网站优化费用,想做电商运营怎么入手文章目录 项目地址一、外部导入打卡功能1.1 创建实体1. Entry实体2. EntryImport实体3. 添加数据库配置4. 创建表 1.2 创建DTOs1.3 创建GetEnties Controller 二、游标分页2.1 创建所需要的DTOs1. 创建游标分页的请求参数2. 创建CollectionResponse3. 添加游标编码和解码的DTO … 文章目录 项目地址一、外部导入打卡功能1.1 创建实体1. Entry实体2. EntryImport实体3. 添加数据库配置4. 创建表 1.2 创建DTOs1.3 创建GetEnties Controller 二、游标分页2.1 创建所需要的DTOs1. 创建游标分页的请求参数2. 创建CollectionResponse3. 添加游标编码和解码的DTO 2.2 创建游标查询的Controller1. 传入带游标的query2. 对query里的游标解码查询3. 数据查询逻辑2.3 测试 三、Refit3.1 安装需要的包3.2 创建接口IGitHubApi3.3 创建RefitGitHubService3.4 修改使用方法 四、Http resilience4.1 安装所需要的包4.2 创建resilience pipeline简单版4.3 创建全局的resilience处理1. 创建清理全局ResilienceHandler2. 添加全局resilience3. 添加自定义的resilience策略4. 使用自定义策略 XXX1.5 创建缓存属性过滤器Idempotent Request1. 创建户型过滤器2. 创建CreateEntry Controller 1.6 批量提交Entry1. 创建Dto2. 创建批量提交的Controller 1.7 Quartz后台任务调度1. 给Habit添加AutomationSource2. 创建定时任务3. 创建GitHubHabitProcessorJob 1.8 Hateoas Driven1. 前后端解耦2. 后端控制前端页面 项目地址 教程作者教程地址 代码仓库地址 所用到的框架和插件 dbt airflow一、外部导入打卡功能 1.1 创建实体 1. Entry实体 用来记录打卡 public sealed class Entry {public string Id { get; set; } // 条目的唯一标识符例如 e_xxxxxpublic string HabitId { get; set; } // 所属的习惯 ID外键关联 Habit 表public string UserId { get; set; } // 该条目所属用户的 IDpublic int Value { get; set; } // 数值比如某习惯的完成次数、时长等public string? Notes { get; set; } // 备注信息可选填写public EntrySource Source { get; init; } // 条目的来源手动、自动、文件导入等public string? ExternalId { get; init; } // 外部系统的 ID用于去重或跟踪导入来源public bool IsArchived { get; set; } // 是否被归档不再显示在活跃列表中public DateOnly Date { get; set; } // 条目的日期例如 2025-04-21public DateTime CreatedAtUtc { get; set; } // 创建时间UTC 时间public DateTime? UpdatedAtUtc { get; set; } // 最后更新时间可为空public Habit Habit { get; set; } // 导航属性用于 Entity Framework 中的关联查询public static string NewId(){return $e_{Guid.CreateVersion7()}; // 使用 Guid v7 创建带前缀的唯一 ID例如 e_018fa...} }public enum EntrySource {Manual 0, // 手动添加Automation 1, // 通过系统自动生成例如 GitHub 数据等FileImport 2 // 通过导入文件生成 }2. EntryImport实体 通过文件上传打卡记录 public sealed class EntryImportJob {public string Id { get; set; } // 导入任务的唯一标识符例如 ei_xxxxxpublic string UserId { get; set; } // 执行导入任务的用户 IDpublic EntryImportStatus Status { get; set; } // 当前导入任务的状态等待/处理中/完成/失败public string FileName { get; set; } // 上传的文件名称如 habits.csvpublic byte[] FileContent { get; set; } // 文件的原始内容二进制格式便于存储/处理public int TotalRecords { get; set; } // 文件中预期要导入的记录总数public int ProcessedRecords { get; set; } // 实际已处理的记录数量public int SuccessfulRecords { get; set; } // 成功导入的记录数public int FailedRecords { get; set; } // 导入失败的记录数public Liststring Errors { get; set; } []; // 导入过程中的错误信息集合public DateTime CreatedAtUtc { get; set; } // 创建时间上传时间使用 UTCpublic DateTime? CompletedAtUtc { get; set; } // 完成时间仅在导入完成后有值public static string NewId(){return $ei_{Guid.CreateVersion7()}; // 使用 Guid v7 创建带前缀的唯一 ID例如 ei_018fa...} }public enum EntryImportStatus {Pending, // 等待开始处理Processing, // 正在处理Completed, // 处理完成Failed // 处理失败可能是文件损坏、格式错误等 }3. 添加数据库配置 Entry表 public sealed class EntryConfiguration : IEntityTypeConfigurationEntry {public void Configure(EntityTypeBuilderEntry builder){builder.HasKey(e e.Id);builder.Property(e e.Id).HasMaxLength(500);builder.Property(e e.HabitId).HasMaxLength(500);builder.Property(e e.UserId).HasMaxLength(500);builder.Property(e e.Notes).HasMaxLength(1000);builder.Property(e e.ExternalId).HasMaxLength(1000);builder.HasOne(e e.Habit).WithMany().HasForeignKey(e e.HabitId);builder.HasOneUser().WithMany().HasForeignKey(e e.UserId);// We have to match snake_case naming convention for the column namebuilder.HasIndex(e e.ExternalId).IsUnique().HasFilter(external_id IS NOT NULL);} }4. 创建表 进行迁移 Add-Migration Add_Entry -Context ApplicationDbContext -o Migrations/Application1.2 创建DTOs 1.3 创建GetEnties Controller 创建GetEntries流程梳理 1. 传入请求的参数 [FromQuery] EntriesQueryParameters query以及需要的服务 2. 获取当前用户信息并且判断用户是否存在 3. 验证EntriesQueryParameters 里的Sort和Fields是否合法 4. 根据排序信息进行排序映射 5. 使用IQueryable 延迟查询 6. 获取所有数据条数用于分页 7. 进行排序查询 8. 对查询结果进行分页 9. 判断是否有Hateoas 10.返回结果 [HttpGet]public async TaskIActionResult GetEntries([FromQuery] EntriesQueryParameters query,SortMappingProvider sortMappingProvider,DataShapingService dataShapingService){string? userId await userContext.GetUserIdAsync();if (string.IsNullOrWhiteSpace(userId)){return Unauthorized();}if (!sortMappingProvider.ValidateMappingsEntryDto, Entry(query.Sort)){return Problem(statusCode: StatusCodes.Status400BadRequest,detail: $The provided sort parameter isnt valid: {query.Sort});}if (!dataShapingService.ValidateEntryDto(query.Fields)){return Problem(statusCode: StatusCodes.Status400BadRequest,detail: $The provided data shaping fields arent valid: {query.Fields});}SortMapping[] sortMappings sortMappingProvider.GetMappingsEntryDto, Entry();IQueryableEntry entriesQuery dbContext.Entries.Where(e e.UserId userId).Where(e query.HabitId null || e.HabitId query.HabitId).Where(e query.FromDate null || e.Date query.FromDate).Where(e query.ToDate null || e.Date query.ToDate).Where(e query.Source null || e.Source query.Source).Where(e query.IsArchived null || e.IsArchived query.IsArchived);int totalCount await entriesQuery.CountAsync();ListEntryDto entries await entriesQuery.ApplySort(query.Sort, sortMappings).Skip((query.Page - 1) * query.PageSize).Take(query.PageSize).Select(EntryQueries.ProjectToDto()).ToListAsync();var paginationResult new PaginationResultExpandoObject{Items dataShapingService.ShapeCollectionData(entries,query.Fields,query.IncludeLinks ? e CreateLinksForEntry(e.Id, query.Fields, e.IsArchived) : null),Page query.Page,PageSize query.PageSize,TotalCount totalCount};if (query.IncludeLinks){paginationResult.Links CreateLinksForEntries(query,paginationResult.HasNextPage,paginationResult.HasPreviousPage);}return Ok(paginationResult);}二、游标分页 2.1 创建所需要的DTOs 1. 创建游标分页的请求参数 Curor主要是一个index用来记录上一页的位置 namespace DevHabit.Api.DTOs.Entries; public sealed record EntriesCursorQueryParameters : AcceptHeaderDto {public string? Cursor { get; init; }public string? Fields { get; init; }public string? HabitId { get; init; }public DateOnly? FromDate { get; init; }public DateOnly? ToDate { get; init; }public EntrySource? Source { get; init; }public bool? IsArchived { get; init; }public int Limit { get; init; } 10; }2. 创建CollectionResponse 该实体用来表示表示含有items和links的实体 namespace DevHabit.Api.DTOs.Common; public sealed class CollectionResponseT : ICollectionResponseT, ILinksResponse {public ListT Items { get; init; }public ListLinkDto Links { get; set; } }数据结构为 3. 添加游标编码和解码的DTO 将最后一条数据的Id和时间进行base64的编码和解码防止数据泄密 namespace DevHabit.Api.DTOs.Entries; public sealed record EntryCursorDto(string Id, DateOnly Date) {//将一个游标ID 和时间编码为字符串前端分页请求时可用public static string Encode(string id, DateOnly date){var cursor new EntryCursorDto(id, date); // 创建一个游标对象string json JsonSerializer.Serialize(cursor); // 序列化为 JSON 字符串return Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(json)); // 转成 Base64避免 JSON 暴露或格式错误}public static EntryCursorDto Decode(string? cursor){if (string.IsNullOrWhiteSpace(cursor)){return null;}try{string json Base64UrlEncoder.Decode(cursor); //解码 Base64 字符串return JsonSerializer.DeserializeEntryCursorDto(json); // 反序列化回游标对象}catch{return null;}} }2.2 创建游标查询的Controller 流程梳理 1. 传入带游标的query 2. 对query里的游标解码查询 如果携带了游标对游标进行解码并且根据游标的信息查询数据 3. 数据查询逻辑 获取比Limit多的数据11条如果数据大于10条说明还有下一页将最后一条数据的id和Date编码为下一个游标去除掉多余的1的数据保证每次10条将数据返回给前端 2.3 测试 当我们发起一个需要100条数据的请求如果还有下一页的数据将会得到有next-page的links 三、Refit 3.1 安装需要的包 3.2 创建接口IGitHubApi 用 Refit 自动帮你生成访问 GitHub API 的客户端 namespace DevHabit.Api.Services;//每次调用这个接口的时候自动给 HTTP 请求带上这两个头 [Headers(User-Agent: DevHabit/1.0, Accept: application/vnd.githubjson)] public interface IGitHubApi {[Get(/user)] //GET 请求访问的是 GitHub API 的 /user 路径TaskApiResponseGitHubUserProfileDto GetUserProfile([Authorize(scheme: Bearer)] string accessToken, //自动添加jwt Token在请求头中CancellationToken cancellationToken default);[Get(/users/{username}/events)] //GET 请求访问的是 GitHub API 的 /users/{username}/events 路径TaskApiResponseIReadOnlyListGitHubEventDto GetUserEvents(string username,[Authorize(scheme: Bearer)] string accessToken, // accessToken自动插到请求头里带身份认证int page 1,[AliasAs(per_page)] int perPage 100, //告诉RefitGitHub API 要求参数名是 per_page不是 C# 里的驼峰 PerPageCancellationToken cancellationToken default); }注册该服务 3.3 创建RefitGitHubService using System.Net.Http.Headers; using DevHabit.Api.DTOs.GitHub; using Newtonsoft.Json; using Refit;namespace DevHabit.Api.Services;public sealed class RefitGitHubService(IGitHubApi gitHubApi, ILoggerGitHubService logger) {public async TaskGitHubUserProfileDto? GetUserProfileAsync(string accessToken,CancellationToken cancellationToken default){ArgumentException.ThrowIfNullOrEmpty(accessToken);ApiResponseGitHubUserProfileDto response await gitHubApi.GetUserProfile(accessToken, cancellationToken);if (!response.IsSuccessStatusCode){logger.LogWarning(Failed to get user profile from GitHub. Status code: {StatusCode}, response.StatusCode);return null;}return response.Content;}public async TaskIReadOnlyListGitHubEventDto? GetUserEventsAsync(string username,string accessToken,int page 1,int perPage 100,CancellationToken cancellationToken default){ArgumentException.ThrowIfNullOrEmpty(accessToken);ArgumentException.ThrowIfNullOrEmpty(username);ApiResponseIReadOnlyListGitHubEventDto response await gitHubApi.GetUserEvents(username,accessToken,page,perPage,cancellationToken);if (!response.IsSuccessStatusCode){logger.LogWarning(Failed to get user events from GitHub. Status code: {StatusCode}, response.StatusCode);return null;}return response.Content;} }注册该服务在DependencyInjection //注册RefitGitHubService builder.Services.AddTransientRefitGitHubService();3.4 修改使用方法 替换之前使用githubService方法的Controller 四、Http resilience 4.1 安装所需要的包 4.2 创建resilience pipeline简单版 直接给需要使用的地方添加这里我们使用refit获取第三方github的api数据所以在该服务后面添加 4.3 创建全局的resilience处理 1. 创建清理全局ResilienceHandler 如果我们配置了全局resilience但是部分服务又想执行自己的熔断措施就需要先清理当前全局的措施在添加自己的 namespace DevHabit.Api.Extensions; public static class ResilienceHttpClientBuilderExtensions {public static IHttpClientBuilder InternalRemoveAllResilienceHandlers(this IHttpClientBuilder builder){builder.ConfigureAdditionalHttpMessageHandlers(static (handlers, _) {for (int i handlers.Count - 1; i 0; i--){if (handlers[i] is ResilienceHandler){handlers.RemoveAt(i);}}});return builder;} }使用 在需要清除的服务先清除 2. 添加全局resilience 直接在服务里使用微软的包即可 3. 添加自定义的resilience策略 如果上面的包里的方法不够使用我们可以添加自己的策略创建自己的测试策略在 HttpClient 发送每一个请求前强制延迟 10 秒再发送。 namespace DevHabit.Api.Services;public sealed class DelayHandler : DelegatingHandler {protected override async TaskHttpResponseMessage SendAsync(HttpRequestMessage request,CancellationToken cancellationToken){await Task.Delay(10000, cancellationToken);return await base.SendAsync(request, cancellationToken);} }4. 使用自定义策略 XXX 1.5 创建缓存属性过滤器Idempotent Request 这个特性让你的某些接口如创建订单、提交支付变成“只处理一次”防止用户刷新或网络重复请求时多次处理。 1. 创建户型过滤器 创建Services namespace DevHabit.Api.Services;[AttributeUsage(AttributeTargets.Method)] public sealed class IdempotentRequestAttribute : Attribute, IAsyncActionFilter {private const string IdempotenceKeyHeader Idempotency-Key;private static readonly TimeSpan DefaultCacheDuration TimeSpan.FromMinutes(60);public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){if (!context.HttpContext.Request.Headers.TryGetValue(IdempotenceKeyHeader,out StringValues idempotenceKeyValue) ||!Guid.TryParse(idempotenceKeyValue, out Guid idempotenceKey)){ProblemDetailsFactory problemDetailsFactory context.HttpContext.RequestServices.GetRequiredServiceProblemDetailsFactory();ProblemDetails problemDetails problemDetailsFactory.CreateProblemDetails(context.HttpContext,statusCode: StatusCodes.Status400BadRequest,title: Bad Request,detail: $Invalid or missing {IdempotenceKeyHeader} header);context.Result new BadRequestObjectResult(problemDetails);return;}// In production code you would want to use some kind of distributed cache. This is just for proof of concept. IMemoryCache cache context.HttpContext.RequestServices.GetRequiredServiceIMemoryCache();string cacheKey $IdempotentRequest:{idempotenceKey};int? statusCode cache.Getint?(cacheKey);if (statusCode is not null){var result new StatusCodeResult(statusCode.Value);context.Result result;return;}ActionExecutedContext executedContext await next();if (executedContext.Result is ObjectResult objectResult){cache.Set(cacheKey, objectResult.StatusCode, DefaultCacheDuration);}} }2. 创建CreateEntry Controller 在CreateEntry添加修饰器 [HttpPost][IdempotentRequest]public async TaskActionResultEntryDto CreateEntry(CreateEntryDto createEntryDto,[FromHeader] AcceptHeaderDto acceptHeader,IValidatorCreateEntryDto validator){string? userId await userContext.GetUserIdAsync();if (string.IsNullOrWhiteSpace(userId)){return Unauthorized();}await validator.ValidateAndThrowAsync(createEntryDto);Habit? habit await dbContext.Habits.FirstOrDefaultAsync(h h.Id createEntryDto.HabitId h.UserId userId);if (habit is null){return Problem(detail: $Habit with ID {createEntryDto.HabitId} does not exist.,statusCode: StatusCodes.Status400BadRequest);}Entry entry createEntryDto.ToEntity(userId, habit);dbContext.Entries.Add(entry);await dbContext.SaveChangesAsync();EntryDto entryDto entry.ToDto();if (acceptHeader.IncludeLinks){entryDto.Links CreateLinksForEntry(entry.Id, null, entry.IsArchived);}return CreatedAtAction(nameof(GetEntry), new { id entryDto.Id }, entryDto);}前端请求时带上一个唯一的 Header会根据Idempotency-Key进行判断如果重复或者没有报错 POST /orders Idempotency-Key: d2b7e35c-5e23-4e1d-94cf-3acda6b9b5b31.6 批量提交Entry 1. 创建Dto namespace DevHabit.Api.DTOs.Entries; public sealed record CreateEntryBatchDto {public required ListCreateEntryDto Entries { get; init; } } 2. 创建批量提交的Controller [HttpPost(batch)]public async TaskActionResultListEntryDto CreateEntryBatch(CreateEntryBatchDto createEntryBatchDto,[FromHeader] AcceptHeaderDto acceptHeader,IValidatorCreateEntryBatchDto validator){string? userId await userContext.GetUserIdAsync();if (string.IsNullOrWhiteSpace(userId)){return Unauthorized();}await validator.ValidateAndThrowAsync(createEntryBatchDto);//收集所有 entry 中使用到的 HabitId防止重复var habitIds createEntryBatchDto.Entries.Select(e e.HabitId).ToHashSet();//从数据库中查询这些习惯是否属于当前用户ListHabit existingHabits await dbContext.Habits.Where(h habitIds.Contains(h.Id) h.UserId userId).ToListAsync();//如果查询到的习惯数量和传入的习惯ID数量不一致说明有无效的习惯IDif (existingHabits.Count ! habitIds.Count){return Problem(detail: One or more habit IDs is invalid,statusCode: StatusCodes.Status400BadRequest);}//将 DTO 转换为 Entity 对象准备写入数据库var entries createEntryBatchDto.Entries.Select(dto dto.ToEntity(userId, existingHabits.First(h h.Id dto.HabitId))).ToList();dbContext.Entries.AddRange(entries);await dbContext.SaveChangesAsync();//把 Entity 转换为返回给前端的 DTOvar entryDtos entries.Select(e e.ToDto()).ToList();if (acceptHeader.IncludeLinks){foreach (EntryDto entryDto in entryDtos){entryDto.Links CreateLinksForEntry(entryDto.Id, null, entryDto.IsArchived);}}return CreatedAtAction(nameof(GetEntries), entryDtos);}1.7 Quartz后台任务调度 安装Quartz PackageVersion IncludeQuartz.Extensions.Hosting Version3.14.0 /使用教程以及持久化方案 https://www.bilibili.com/video/BV1c8AVerEQY/?spm_id_from333.337.search-card.all.clickvd_source791e6deaa9c8a56b1f845a0bc1431b711. 给Habit添加AutomationSource 添加AutomationSource枚举类型字段表示自动化数据来源 2. 创建定时任务 定期扫描数据库中启用了 GitHub 自动化的习惯Habit数据为每个符合条件的 habit 创建并立即执行一个 GitHubHabitProcessorJob处理任务。 namespace DevHabit.Api.Jobs;[DisallowConcurrentExecution] public sealed class GitHubAutomationSchedulerJob(ApplicationDbContext dbContext,ILoggerGitHubAutomationSchedulerJob logger) : IJob {public async Task Execute(IJobExecutionContext context){try{logger.LogInformation(Starting GitHub automation scheduler job);//找到所有是AutomationSource1的集合ListHabit habitsToProcess await dbContext.Habits.Where(h h.AutomationSource AutomationSource.GitHub !h.IsArchived).ToListAsync(context.CancellationToken);logger.LogInformation(Found {Count} habits with GitHub automation, habitsToProcess.Count);foreach (Habit habit in habitsToProcess){// Create a trigger for immediate executionITrigger trigger TriggerBuilder.Create().WithIdentity($github-habit-{habit.Id}, github-habits).StartNow().Build();// Create the job with habit dataIJobDetail jobDetail JobBuilder.CreateGitHubHabitProcessorJob().WithIdentity($github-habit-{habit.Id}, github-habits).UsingJobData(habitId, habit.Id).Build();// Schedule the jobawait context.Scheduler.ScheduleJob(jobDetail, trigger, context.CancellationToken);logger.LogInformation(Scheduled processor job for habit {HabitId}, habit.Id);}logger.LogInformation(Completed GitHub automation scheduler job);}catch (Exception ex){logger.LogError(ex, Error executing GitHub automation scheduler job);throw;}} }3. 创建GitHubHabitProcessorJob using DevHabit.Api.Database; using DevHabit.Api.DTOs.GitHub; using DevHabit.Api.Entities; using DevHabit.Api.Services; using Microsoft.EntityFrameworkCore; using Quartz;namespace DevHabit.Api.Jobs;public sealed class GitHubHabitProcessorJob(ApplicationDbContext dbContext,GitHubAccessTokenService gitHubAccessTokenService,RefitGitHubService gitHubService,ILoggerGitHubHabitProcessorJob logger) : IJob {private const string PushEventType PushEvent;public async Task Execute(IJobExecutionContext context){//取出当前任务对应的 HabitIdstring habitId context.JobDetail.JobDataMap.GetString(habitId)?? throw new InvalidOperationException(HabitId not found in job data);try{logger.LogInformation(Processing GitHub events for habit {HabitId}, habitId);// Get the habit and ensure it still exists and is configured for GitHub automationHabit? habit await dbContext.Habits.FirstOrDefaultAsync(h h.Id habitId h.AutomationSource AutomationSource.GitHub !h.IsArchived, context.CancellationToken);if (habit null){logger.LogWarning(Habit {HabitId} not found or no longer configured for GitHub automation, habitId);return;}// Get the users GitHub access tokenstring? accessToken await gitHubAccessTokenService.GetAsync(habit.UserId, context.CancellationToken);if (string.IsNullOrWhiteSpace(accessToken)){logger.LogWarning(No GitHub access token found for user {UserId}, habit.UserId);return;}// Get GitHub profileGitHubUserProfileDto? profile await gitHubService.GetUserProfileAsync(accessToken, context.CancellationToken);if (profile null){logger.LogWarning(Couldnt retrieve GitHub profile for user {UserId}, habit.UserId);return;}// Get GitHub eventsListGitHubEventDto gitHubEvents [];const int perPage 100;const int pagesToFetch 10;//调用 Refit 封装的 GitHub API 拿到用户最近的 GitHub 活动默认最多抓取 1000 条for (int page 1; page pagesToFetch; page){IReadOnlyListGitHubEventDto? pageEvents await gitHubService.GetUserEventsAsync(profile.Login,accessToken,page,perPage,context.CancellationToken);if (pageEvents is null || !pageEvents.Any()){break;}gitHubEvents.AddRange(pageEvents);}if (!gitHubEvents.Any()){logger.LogWarning(Couldnt retrieve GitHub events for user {UserId}, habit.UserId);return;}// Filter to push eventsvar pushEvents gitHubEvents.Where(e e.Type PushEventType).ToList();logger.LogInformation(Found {Count} push events for habit {HabitId}, pushEvents.Count, habitId);foreach (GitHubEventDto gitHubEventDto in pushEvents){// Check if we already have an entry for this eventbool exists await dbContext.Entries.AnyAsync(e e.HabitId habitId e.ExternalId gitHubEventDto.Id,context.CancellationToken);if (exists){logger.LogDebug(Entry already exists for event {EventId}, gitHubEventDto.Id);continue;}// Create a new entryvar entry new Entry{Id $e_{Guid.CreateVersion7()},HabitId habitId,UserId habit.UserId,Value 1, // Each push counts as 1Notes ${gitHubEventDto.Actor.Login} pushed:{string.Join(Environment.NewLine,//从 GitHub 的 PushEvent 中提取 commit 信息gitHubEventDto.Payload.Commits?.Select(c $- {c.Message}) ?? [])},Date DateOnly.FromDateTime(gitHubEventDto.CreatedAt),Source EntrySource.Automation,ExternalId gitHubEventDto.Id,CreatedAtUtc DateTime.UtcNow};dbContext.Entries.Add(entry);logger.LogInformation(Created entry for event {EventId} on habit {HabitId},gitHubEventDto.Id,habitId);}//保存到数据库await dbContext.SaveChangesAsync(context.CancellationToken);}catch (Exception ex){logger.LogError(ex, Error processing GitHub events for habit {HabitId}, habitId);throw;}} } 1.8 Hateoas Driven 场景 切换标签显示该页面的links根据当前页面的某些条件例如tags是否大于5条控制create 按钮是否显示 1. 前后端解耦 在展示用户的习惯列表时根据后端返回的 self 链接动态生成每个习惯详情页的路由跳转地址让前端和后端解耦前端不需要硬编码 URL Link key{habit.links.find(ll.rel ‘self)?.href}to{new URL(habit.links.find(ll.relself)?.href??’#).pathname}实际生产的链接是 Link keyhttps://api.example.com/habits/abc123 to/habits/abc1232. 后端控制前端页面 通过控制CreateLinksTags的返回内容来控制
http://www.pierceye.com/news/759702/

相关文章:

  • 做网站的详细步骤叫别人做网站权重被转移了
  • 做网站好还是网店做网站是怎样赚钱的
  • 国内网站 备案北京模板网站建站
  • 怎么建立网站?婚纱网站策划书模板下载
  • 接单子做网站词类似酷家乐做庭院的网站
  • 道路建设网站专题推广做黄页网站
  • 做展柜平时在哪里网站推广青岛原创工程设计有限公司
  • 网站建设加网络营销营销网站有多种类型
  • 深圳网站网页制作公司深圳品牌网站建设公司有哪些
  • 网站建设中 windows网站后台用什么做
  • 外贸营销型网站建站怎么做便民信息网站
  • 事业单位门户网站建设的建议大连建设工程信息网华宇凤凰城东侧市政管网配套工程
  • 上海网站建设开发哪亚马逊官网首页中国
  • 常德网站建设套餐报价英文网站字体大小
  • 橙色网站logo 配色播放器网站怎么做
  • dw网站制作怎样做网站xml
  • 房屋租赁网站开发意义新网站如何做排名
  • 钉钉如何做自己的网站银川企业网站建设
  • 做游戏女角色去衣的网站网站建设及售后服务的说明书
  • 微网站下载资料怎么做网站开发毕业设计任务书怎么写
  • ckplayer网站根目录泉州 网站制作
  • 中国建设银行网站江苏分行帮别人做网站收多少钱合适
  • 公司该建哪种网站带有互动的网站开发
  • 怎样进入谷歌网站怎么做一个简易网站
  • 邯郸网站优化公司集团公司简介模板
  • 网站的需求分析怎么写文山州住房建设网站
  • 广东网站开发费用动易的网站能否静态
  • 网站的后期维护php建设图书网站代码
  • 做营销网站制作外贸多语言网站建设
  • 广州做网站 信科网络wordpress优化宝塔