【.Net Core/.Net8教程】(三)如何优雅地校验参数

FluentValidation是一个流行的开源验证库,用于在.NET应用程序中验证输入数据。
它有如下特点

  • 语法简洁
  • 强大的验证功能
  • 支持多语言和本地化
  • 可重用性和模块化
  • 易于集成
  • 详细的错误消息
  • 扩展性高
    借助FluentValidation,我们可以达到优雅地校验参数的目的。

环境准备:Install-Package FluentValidation -version 11.9.1

目标框架:.Net8

  1. 创建验证器类: 创建一个继承自AbstractValidator<T>的类,其中T是要验证的模型类型。

        public class AccountModel
        {
            public string Name { get; set; }
        }
    
    	public class AccountModelValidator : AbstractValidator<AccountModel>
        {
            public AccountModelValidator()
            {
                RuleFor(e => e.Name).NotNull();
            }
        }
    
  2. 在应用程序中使用验证器: 在需要验证输入的地方,实例化验证器类并调用Validate方法。

            [HttpPost]
            public dynamic GetData(AccountModel dto)
            {
                AccountModelValidator validator = new AccountModelValidator();
                ValidationResult result = validator.Validate(dto);
                return Ok(result);
            }
    

    完成这两步,就可以创建第一个简单例子了。

    当AccountModel.Name=null时,查看最终ValidationResult结果。FluentValidation会根据规则校验参数不能为空,并返回多项校验信息

    {
      "isValid": false,
      "errors": [
        {
          "propertyName": "Name",
          "errorMessage": "'Name' must not be empty.",
          "attemptedValue": null,
          "customState": null,
          "severity": 0,
          "errorCode": "NotNullValidator",
          "formattedMessagePlaceholderValues": {
            "PropertyName": "Name",
            "PropertyValue": null,
            "PropertyPath": "Name"
          }
        }
      ],
      "ruleSetsExecuted": [
        "default"
      ]
    }
    
  3. 注册:手动new validator不是一个好思想,可以注册后使用依赖注入

    手动注册

    builder.Services.AddTransient<IValidator<AccountModel>, AccountModelValidator>();
    //或
    builder.Services.AddValidatorsFromAssemblyContaining<AccountModelValidator>();
    //或
    builder.Services.AddValidatorsFromAssemblyContaining(typeof(AccountModelValidator));
    

    或自动注册

    需要引入nuget包FluentValidation.AspNetCore

    Install-Package FluentValidation.AspNetCore

    builder.Services.AddValidatorsFromAssembly(Assembly.Load("SomeAssembly"));
    //或
    builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
    
  4. 依赖注入

            private readonly IValidator<AccountModel> _validator;
    
            public ExampleController(IValidator<AccountModel> validator)
            {
                this._validator = validator;
            }
    
            [HttpPost]
            public dynamic GetData(AccountModel dto)
            {
                ValidationResult result = _validator.Validate(dto);
                return Ok(result);
            }
    
  5. 自定义验证错误消息: 使用WithMessage方法指定自定义错误消息。

    自定义错误消息

    RuleFor(e => e.Name).NotNull().WithMessage("名字不能为空");
    

    使用占位符

    RuleFor(address => address.Postcode).NotEmpty().WithMessage(address => string.Format("国家:{0},城市:{1}的邮编不能为空", address.Country, address.City));
    //或
    RuleFor(address => address.Postcode).NotEmpty().WithMessage(address => $"国家:{address.Country},城市:{address.City}的邮编不能为空");
    //"errorMessage": "国家:China,城市:深圳的邮编不能为空",
    

    重写属性名

    RuleFor(address => address.Postcode).NotEmpty().WithName("邮编");
    //"errorMessage": "'邮编' must not be empty."
    

    全局重写属性名

    //全部被验证的类Country属性名都会被替换
    ValidatorOptions.Global.DisplayNameResolver = (type, member, expression) =>
    {
        if (member?.Name=="Country")
            return "国家";
        return null;
    };//'国家' must not be empty.
    
  6. 校验失败时抛异常:异常需要捕捉

    _validator.ValidateAndThrow(dto);
    
  7. 级联验证: 在一个属性的验证规则中调用其他属性的验证规则。

    RuleFor(x => x.EndDate).GreaterThan(x => x.StartDate).WithMessage("结束日期必须大于开始日期.");
    

    默认情况下,验证规则会执行所有验证规则,如果失败时要停止执行后续的验证,则可使用CascadeMode.Stop模式

    RuleFor(x => x.Country).Cascade(CascadeMode.Stop).NotNull().NotEqual("China");//任何失败都会停止执行,只会返回第一个失败结果
    
  8. 自定义规则集:规则集允许将验证规则组合在一起

    假设,如果同时有两个接口需要校验同一个类,但规则不同怎么办呢。

    举例,A接口要求Name参数长度必须大于5小于20,B接口要求Name长度在2-10之间。

    这时可以定义两套规则集,分别指定不同命名。在不同接口使用不同规则集时,根据名称指定使用。

        public class AccountModelValidator : AbstractValidator<AccountModel>
        {
            public AccountModelValidator()
            {
                RuleSet("short", () =>
                {
                    RuleFor(e => e.Name).NotEmpty().Must(x => x.Length >= 2 && x.Length <= 10).WithMessage("名字长度必须在2-10之间");
    
                });
                RuleSet("long", () =>
                {
                    RuleFor(e => e.Name).NotEmpty().Must(x => x.Length > 5 && x.Length < 20).WithMessage("名字长度必须大于5小于20");
    
                });
                RuleFor(e => e.Age).NotEmpty().LessThan(18);//规则集外的
            }
        }
    

    具体使用时可以指定规则集:

    只执行指定规则集,非指定规则集、和规则集外的均不执行

    var result = _validator.Validate(dto, options =>options.IncludeRuleSets("short");//A接口
    var result = _validator.Validate(dto, options =>options.IncludeRuleSets("long");//B接口                         
    

    多个规则集一起执行

    var result = _validator.Validate(dto, options =>options.IncludeRuleSets("short", "long"));
    

    执行涉及某个属性的所有规则集

    var result = _validator.Validate(dto, options => options.IncludeProperties(e=>e.Name);//只要规则集内有对Name的约束,这些规则都会执行校验
    

    执行所有规则集合

    var result = _validator.Validate(dto, options => options.IncludeAllRuleSets();
    

    执行指定规则集和规则集外的验证规则

    var result = _validator.Validate(dto, options => options.IncludeRuleSets("short").IncludeRulesNotInRuleSet());
    
  9. 条件验证:

    使用顶级语句When

    When(e => e.Country == "China", () => RuleFor(addr => addr.Postcode).Must(addr => addr.Length == 6).WithMessage(address => "中国的邮编长度须为6"));
    When(e => e.Country == "America", () => RuleFor(addr => addr.Postcode).Must(addr => addr.Length == 9).WithMessage(address => "美国的邮编长度须为9"));
    
    //其他例子
    RuleFor(customer => customer.Address.Postcode).NotNull().When(customer => customer.Address != null);
    RuleFor(x => x.Age).GreaterThan(18).When(x => x.IsAdult);
    

    使用Otherwise表示否则条件

    When(e => e.Country == "China", () => RuleFor(addr => addr.Postcode).NotEmpty().Must(addr => addr.Length == 6).WithMessage(address => "中国的邮编长度须为6"))
        .Otherwise(()=> RuleFor(addr => addr.Postcode).NotEmpty().WithName("邮编"));
    

    仅将条件应用与前述验证器,则要添加ApplyConditionTo.CurrentValidator,否则条件的执行顺序和最终结果,会与意想中不一致

    RuleFor(address => address.Postcode).NotEmpty()//, ApplyConditionTo.CurrentValidator
        .Must(e => e.Length == 6).When(e => e.Country == "China", ApplyConditionTo.CurrentValidator).WithMessage(address => "中国的邮编长度须为6")
        .Must(e => e.Length == 9).When(e => e.Country == "America", ApplyConditionTo.CurrentValidator).WithMessage(address => "美国的邮编长度须为9");
    
  10. 复合属性验证:当出现复合的类属性,且类属性自定义了验证器,一个验证器中可以复用另一个验证器

        
    	public class Address
        {
            public string City { get; set; }
            public string Town { get; set; }
            public string Postcode { get; set; }//邮编
        }
        public class AddressValidator : AbstractValidator<Address>
        {
            public AddressValidator()
            {
                RuleFor(address => address.Postcode).NotNull();
            }
        }
    
        public class AccountModel
        {
            public string Name { get; set; }
            public Address Address { get; set; }//地址
        }
    
        public class AccountModelValidator : AbstractValidator<AccountModel>
        {
            public AccountModelValidator()
            {
                RuleFor(e => e.Name).NotNull();
                //RuleFor(e => e.Address).NotNull();//如需先校验Address是否为空,则需添加此句
                RuleFor(e => e.Address).SetValidator(new AddressValidator());//相当于RuleFor(e => e.Address.Postcode).NotNull().When(e => e.Address != null);
            }
        }
    
  11. 集合元素验证:可以对集合中每个元素进行验证

        public class AccountModel
        {
            public string Name { get; set; }
            public int? Age { get; set;}
            public List<string> PhoneNumbers { get; set; }
        }
    
        public class AccountModelValidator : AbstractValidator<AccountModel>
        {
            public AccountModelValidator()
            {
                RuleForEach(x => x.PhoneNumbers).NotEmpty().WithMessage("手机号码不能为空");
            }
        }
    

    也可以配合复合属性,复用验证器;同时也可以使用where条件过滤筛选需要验证的元素

        public class Address
        {
            public string Country { get; set; }
            public string City { get; set; }
            public string Town { get; set; }
            public string Postcode { get; set; }//邮编
        }
        public class AccountModel
        {
            public List<Address> Address { get; set; }
        }
    
        public class AccountModelValidator : AbstractValidator<AccountModel>
        {
            public AccountModelValidator()
            {	//筛选集合中国家为China的元素,验证邮编长度必须为6位
                RuleForEach(x => x.Address).Where(x => x.Country == "China").SetValidator(new AddressValidator());
            }
        }
    
  12. 相等验证:

    相等或不等判断

    RuleFor(address => address.Country).Equal("China");
    RuleFor(address => address.Country).NotEqual("China");
    

    忽略大小写

    RuleFor(address => address.Country).NotEqual("China", StringComparer.OrdinalIgnoreCase);
    
  13. 长度验证:

    //长度限制
    RuleFor(address => address.Country).Length(1, 250);
    //最大长度
    RuleFor(address => address.Country).MaximumLength(250); 
    //最小长度
    RuleFor(address => address.Country).MinimumLength(1);
    
  14. 大小验证:

    RuleFor(product => product.Price).LessThan(100);//小于
    RuleFor(product => product.Price).GreaterThan(0);//大于
    RuleFor(product => product.Price).LessThanOrEqualTo(100);//小于等于
    RuleFor(product => product.Price).GreaterThanOrEqualTo(0);//大于等于
    
    RuleFor(product => product.Price).GreaterThan(product => product.MinPriceLimit);//与其他属性比较大小
    
  15. 正则表达式:

    RuleFor(address => address.Country).Matches(@"^[A-Za-z]+$").WithMessage("国家必须为纯英文字母");
    
  16. 自带邮箱验证:

    RuleFor(address => address.Email).EmailAddress();
    
  17. 枚举验证:

    RuleFor(x => x.ConsumerLevel).IsInEnum();//如果值在非枚举之外,则抛错
    
  18. 区间验证:

    //不含边界值,即大于1、小于100
    RuleFor(x => x.Id).ExclusiveBetween(1,100);//'Id' must be between 1 and 100 (exclusive). You entered 100.
    //含边界值,即大于等于1、小于等于100
    RuleFor(x => x.Id).InclusiveBetween(1,100);//‘Id’ must be between 1 and 10. You entered 0.
    
  19. 小数位数验证:

    RuleFor(x => x.Price).PrecisionScale(4, 2, false).WithMessage("“单价”的位数不得超过 4 位,允许小数点后 2 位。不允许出现5个及以上数字,不允许出现3位及以上小数");
    
    //ignoreTrailingZeros 为false时,小数 123.4500 将被视为具有 7 的精度和 4 的刻度
    //ignoreTrailingZeros 为true时,小数 123.4500 将被视为具有 5 的精度和 2 的刻度。
    
  20. 扩展方法自定义占位符

        public static class ValidatorExtension
        {
            public static IRuleBuilderOptions<T, IList<TElement>> ListCountMustFewerThan<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder, int num)
            {
    
                return ruleBuilder.Must((rootObject, list, context) => {
                    context.MessageFormatter.AppendArgument("MaxCount", num);
                    return list.Count < num;
                })
                .WithMessage("{PropertyName}的元素数量必须少于{MaxCount}个.");
            }
        }
    
    //使用时就方便很多
    RuleFor(x => x.PhoneNumbers).ListCountMustFewerThan(3);
    
  21. 更高控制度的自定义验证器:

                RuleFor(x => x.Country).Custom((element, context) => {
                    if (element != "中国" && element != "美国")
                    {
                        context.AddFailure("其他国家不支持");
                    }
                });
    
  22. 组合验证器:使用Include组合多个验证器

        public class Address
        {
            public int Id { get; set; } 
            public string Country { get; set; }
            public string Postcode { get; set; }//邮编
        }
    
    public class PostcodeValidator : AbstractValidator<Address>//注意继承自同一个AbstractValidator
    {
        public PostcodeValidator()
        {
            RuleFor(address => address.Postcode).NotEmpty().WithMessage("邮编不能为空");
            RuleFor(address => address.Postcode).MaximumLength(10).WithMessage("邮编长度不能超过10");
        }
    }
    public class CountryValidator : AbstractValidator<Address>//注意继承自同一个AbstractValidator
    {
        public CountryValidator()
        {
            RuleFor(address => address.Country).NotEmpty().WithMessage("国家不能为空");
        }
    }
    public class AddressValidator : AbstractValidator<Address>
    {
        public AddressValidator()
        {
            Include(new PostcodeValidator());
            Include(new CountryValidator());
        }
    }
    
  23. 验证指定属性:验证与指定属性有关的规则,其他的不验证

    var result = _addressValidator.Validate(addr, opt => opt.IncludeProperties(x => x.Country));
    

    验证集合中的指定元素的规则:使用通配符索引器 ( [] ) 来指定集合中的元素项

    //验证每个订单的 Cost 属性
    _validator.Validate(customer, options => options.IncludeProperties("Orders[].Cost"));
    
  24. 异步:调用 ValidateAsync 将同时运行同步和异步规则。

    如果验证程序包含异步验证程序或异步条件,请务必始终在验证程序上调用 ValidateAsync,而切勿调用 Validate。如果调用 Validate,则会引发异常。

    var result = await validator.ValidateAsync(customer);
    

    建议:不要使用异步规则,因为 ASP.NET 的验证管道不是异步的。如果将异步规则与 ASP.NET 的自动验证一起使用,则它们将始终同步运行(FluentValidation 10.x 及更早版本)或引发异常(FluentValidation 11.x 及更高版本)。

  25. 设置严重性级别:

    RuleFor(product => product.Price).NotNull().WithSeverity(person => Severity.Warning);
    

    全局设置严重性级别

    ValidatorOptions.Global.Severity = Severity.Info;
    
  26. 设置错误码:

    RuleFor(product => product.Price).NotNull().WithErrorCode("502");   
    
  27. 自定义状态:

    RuleFor(person => person.Name).NotNull().WithState(person => 1);  
    
  28. 本地化多语言:

    public class ProductValidator : AbstractValidator<Product> 
    {
      public ProductValidator(IStringLocalizer<Product> localizer)
      {
        //根据key值以特定语言显示
        RuleFor(product => product.Price).NotNull().WithMessage(x => localizer["PriceNotNullKey"]);
      }
    }
    
    public class CustomLanguageManager : FluentValidation.Resources.LanguageManager
    {
      public CustomLanguageManager() 
      {
        AddTranslation("en", "PriceNotNullKey", "'{PropertyName}' is required.");
        AddTranslation("zh", "PriceNotNullKey", "'{PropertyName}' 是必填项.");
      }
    }
    
    //通过设置 LanguageManager 属性来替换默认的 LanguageManager
    ValidatorOptions.Global.LanguageManager = new CustomLanguageManager();
    //是否禁用本地化
    ValidatorOptions.Global.LanguageManager.Enabled = false;
    //强制默认消息始终以特定语言显示
    ValidatorOptions.Global.LanguageManager.Culture = new CultureInfo("zh");
    
    
  29. 依赖规则:

    默认情况下,FluentValidation 中的所有规则都是独立的,不能相互影响。这是异步验证工作的有意和必要条件。但是,在某些情况下,我们希望确保某些规则仅在另一个规则完成后执行。

    这时可以使用 DependentRules 来执行此操作

    RuleFor(x => x.Name).NotNull().DependentRules(() => {
      RuleFor(x => x.Id).NotNull();
    });
    

    在许多情况下,结合使用 When 条件 CascadeMode 来防止规则运行可能更简单

  30. 自定义异常:

    public class AddressValidator : AbstractValidator<Address>
    {
        public AddressValidator()
        {
            RuleFor(address => address.Postcode).NotEmpty().WithMessage("邮编不能为空");
        }
        
        //自定义异常
        protected override void RaiseValidationException(ValidationContext<Address> context, ValidationResult result)
        {
            var ex = new ValidationException(result.Errors);
            throw new CustomException(ex.Message);
        }
    }
    

    捕捉异常

                try
                {
                    _addressValidator.ValidateAndThrow(addr);
                }
                catch (CustomException ex)
                {
                    //验证不通过时,就会捕捉到自定义异常
                }
    

总结

  1. 在我们的编程习惯中,往往把校验逻辑嵌入到业务代码中,这样其实违背了单一原则。
  2. 在设计项目框架时,应考虑把校验逻辑分离核心业务,虽然校验也是业务的一环,但项目的焦点应该放在核心业务代码上 ,校验逻辑不应干扰到核心业务。
  3. 借助FluentValidation,我们可以很方便地分离校验逻辑,同时由于校验逻辑的分离,可以自由组合、复用模块。因为很多校验逻辑其实是重复的。
  4. 而且模块的复用,也达到了统一校验标准的目的。

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

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

相关文章

解码Starknet Verifier:深入逆向工程之旅

1. 引言 Sandstorm为&#xff1a; 能提交独立proof给StarkWare的Ethereum Verifier&#xff0c;的首个开源的STARK prover。 开源代码见&#xff1a; https://github.com/andrewmilson/sandstorm&#xff08;Rust&#xff09; L2Beat 提供了以太坊上Starknet的合约架构图&…

单链表经典算法

一&#xff0c;移除链表元素 思路一 遍历数组&#xff0c;如果遇到链表中的元素等于val的节点就执行删除操作 typedef struct ListNode ListNode;struct ListNode* removeElements(struct ListNode* head, int val) {if(headNULL){return NULL;} ListNode*pnewhead(ListNode*)m…

14.集合、常见的数据结构

集合 概念 Java中的集合就是一个容器&#xff0c;用来存放Java对象。 集合在存放对象的时候&#xff0c;不同的容器&#xff0c;存放的方法实现是不一样的&#xff0c; Java中将这些不同实现的容器&#xff0c;往上抽取就形成了Java的集合体系。 Java集合中的根接口&#x…

MVC和DDD的贫血和充血模型对比

文章目录 架构区别MVC三层架构DDD四层架构 贫血模型代码示例 充血模型代码示例 架构区别 MVC三层架构 MVC三层架构是软件工程中的一种设计模式&#xff0c;它将软件系统分为 模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;Contro…

前端工程化03-贝壳找房项目案例JavaScript常用的js库

4、项目实战&#xff08;贝壳找房&#xff09; 这个项目包含&#xff0c;基本的ajax请求调用,内容的渲染&#xff0c;防抖节流的基本使用&#xff0c;ajax请求工具类的封装 4.1、项目的接口文档 下述接口文档&#xff1a; 简述内容baseURL&#xff1a;http://123.207.32.32…

SQL——高级教程【菜鸟教程】

SQL连接 左连接&#xff1a;SQL LEFT JOIN 关键字 左表相当于主表&#xff0c;不管与右表匹不匹配都会显示所有数据 右表就只会显示和左表匹配的内容。 //例显示&#xff1a;左表的name&#xff0c;有表的总数&#xff0c;时间 SELECT Websites.name, access_log.count, acc…

【机器学习-15】决策树(Decision Tree,DT)算法介绍:原理与案例实现

前言 决策树算法是机器学习领域中的一种重要分类方法&#xff0c;它通过树状结构来进行决策分析。决策树凭借其直观易懂、易于解释的特点&#xff0c;在分类问题中得到了广泛的应用。本文将介绍决策树的基本原理&#xff0c;包括熵和信息熵的相关概念&#xff0c;以及几种经典的…

上位机开发PyQt5(二)【单行输入框、多行输入框、按钮的信号和槽】

目录 一、单行输入框QLineEdit QLineEdit的方法&#xff1a; 二、多行输入框QTextEdit QTextEdit的方法 三、按钮QPushButton 四、按钮的信号与槽 信号与槽简介&#xff1a; 信号和槽绑定&#xff1a; 使用PyQt的槽函数 一、单行输入框QLineEdit QLineEdit控件可以输入…

双向链表专题

文章目录 目录1. 双向链表的结构2. 双向链表的实现3. 顺序表和双向链表的优缺点分析 目录 双向链表的结构双向链表的实现顺序表和双向链表的优缺点分析 1. 双向链表的结构 注意&#xff1a; 这⾥的“带头”跟前面我们说的“头节点”是两个概念&#xff0c;带头链表里的头节点…

Redis 实战1

SDS Redis 只会使用 C 字符串作为字面量&#xff0c; 在大多数情况下&#xff0c; Redis 使用 SDS &#xff08;Simple Dynamic String&#xff0c;简单动态字符串&#xff09;作为字符串表示。 比起 C 字符串&#xff0c; SDS 具有以下优点&#xff1a; 常数复杂度获取字符串…

JavaEE >> Spring MVC(2)

接上文 本文介绍如何使用 Spring Boot/MVC 项目将程序执行业务逻辑之后的结果返回给用户&#xff0c;以及一些相关内容进行分析解释。 返回静态页面 要返回一个静态页面&#xff0c;首先需要在 resource 中的 static 目录下面创建一个静态页面&#xff0c;下面将创建一个静态…

[嵌入式系统-53]:嵌入式系统集成开发环境大全 ( IAR Embedded Workbench(通用)、MDK(ARM)比较 )

目录 一、嵌入式系统集成开发环境分类 二、由MCU芯片厂家提供的集成开发工具 三、由嵌入式操作提供的集成开发工具 四、由第三方工具厂家提供的集成开发工具 五、开发工具的整合 5.1 Keil MDK for ARM 5.2 IAR Embedded Workbench&#xff08;通用&#xff09;、MDK&…

01.本地工作目录、暂存区、本地仓库三者的工作关系

1.持续集成 1.持续集成CI 让产品可以快速迭代&#xff0c;同时还能保持高质量。 简化工作 2.持续交付 交付 3.持续部署 部署 4.持续集成实现的思路 gitjenkins 5.版本控制系统 1.版本控制系统概述2.Git基本概述3.Git基本命令 2.本地工作目录、暂存区、本地仓库三者的工作关系…

抖音评论区精准获客自动化获客释放双手

挺好用的&#xff0c;评论区自动化快速获客&#xff0c;如果手动点引流涨&#xff0c;那就很耗费时间了&#xff0c;不是吗&#xff1f; 网盘自动获取 链接&#xff1a;https://pan.baidu.com/s/1lpzKPim76qettahxvxtjaQ?pwd0b8x 提取码&#xff1a;0b8x

leetcode84柱状图中最大的矩形

题解&#xff1a; - 力扣&#xff08;LeetCode&#xff09; class Solution {public int largestRectangleArea(int[] heights) {Stack<Integer> stack new Stack<>();int maxArea Integer.MIN_VALUE;for(int i 0;i < heights.length;i){int curHeight hei…

YOLOV8添加SKATTENTION

修改ultralytics.nn.modules._init_.py https://zhuanlan.zhihu.com/p/474599120?utm_sourcezhihu&utm id0 https://blog.csdn.net/weixin 42878111/article/details/136060087 https://blog.csdn.net/gg 51511878/aricle/details/138002223 . 最后输出层不一样。

JAVA面试之MQ

如何保证消息的可靠传输&#xff1f;如果消息丢了怎么办 数据的丢失问题&#xff0c;可能出现在生产者、MQ、消费者中。 &#xff08;1&#xff09;生产者发送消息时丢失&#xff1a; ①生产者发送消息时连接MQ失败 ②生产者发送消息到达MQ后未找到Exchange(交换机) ③生产者发…

一对一WebRTC视频通话系列(一)—— 创建页面并显示摄像头画面

本系列博客主要记录WebRtc实现过程中的一些重点&#xff0c;代码全部进行了注释&#xff0c;便于理解WebRTC整体实现。 一、创建html页面 简单添加input、button、video控件的布局。 <html><head><title>WebRTC demo</title></head><h1>…

单片机编程实例400例大全(100-200)

今天继续分享单片机编程实例第100-200例。 今天的实例会比前面100复杂一些&#xff0c;我大概看了下&#xff0c;很多都具备实际产品的参考价值。 今天继续分享单片机编程实例第100-200例。 今天的实例会比前面100复杂一些&#xff0c;我大概看了下&#xff0c;很多都具备实际…

计算机毕业设计hadoop+spark+hive知识图谱音乐推荐系统 音乐数据分析可视化大屏 音乐爬虫 LSTM情感分析 大数据毕设 深度学习 机器学习

黄河科技学院本科毕业设计 任务书 工 学部 大数据与计算机应用 科教中心 计算机科学与技术 专业 2018 级普本1/专升本1班 学号 学生 指导教师 毕业设计题目 基于实时音乐数据挖掘的个性化推荐系统设计与优化 毕业设计工作内容与基本…
最新文章