分类: C#

  • C#设计:工厂模式

    在学习设计模式的时候发现很多对于工厂模式的例子都是很简单的,例如就是用工厂类直接去创建一些无构造参数的简单的实例,这也的创建方式和我们直接new相应的实例没什么差别,体现不出工厂的优势。当需要创建的实例之间具有复杂的依赖关系或者需要创建模式的实例的时候工厂获取创建实例的优势就出现了。

    例如这样一个例子(来自菜鸟):

    public class ShapeFactory {
    
       //使用 getShape 方法获取形状类型的对象
       public Shape getShape(String shapeType){
          if(shapeType == null){
             return null;
          }
          if(shapeType.equalsIgnoreCase("CIRCLE")){
             return new Circle();
          } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
             return new Rectangle();
          } else if(shapeType.equalsIgnoreCase("SQUARE")){
             return new Square();
          }
          return null;
       }
    }

    这个工厂负责实例化不同的图形,通过传入的字符来new出不同的对象

       public static void main(String[] args) {
          ShapeFactory shapeFactory = new ShapeFactory();
    
          //获取 Circle 的对象,并调用它的 draw 方法
          Shape shape1 = shapeFactory.getShape("CIRCLE");
    
          //调用 Circle 的 draw 方法
          shape1.draw();
    
          //获取 Rectangle 的对象,并调用它的 draw 方法
          Shape shape2 = shapeFactory.getShape("RECTANGLE");
    
          //调用 Rectangle 的 draw 方法
          shape2.draw();
    
          //获取 Square 的对象,并调用它的 draw 方法
          Shape shape3 = shapeFactory.getShape("SQUARE");
    
          //调用 Square 的 draw 方法
          shape3.draw();
       }

    在测试函数这里创建了一个工厂,然后在工厂内传入需要的对象名可以获得这个对象实例,需要不同的实例的时候只要改这个参数即可,如果使用直接创建对象的方式也是只需要改创建对象时的对象名就可以了,这样看上去工厂模式和普通的方式没啥区别。

    下面来看一个复杂一点点的示例:

    假设轿车的制造流程如下:

    1. 创建车身
    2. 安装引擎
    3. 安装座椅
    4. 安装车轮
    5. 安装音响系统

    在轿车工厂中,我们需要按照上述制造流程进行车辆的制造。下面是一个简化版的代码实现:

    public class SedanFactory : CarFactory
    {
        public override Car CreateCar()
        {
            // 创建车身
            Body body = new Body();
    
            // 安装引擎
            Engine engine = new Engine();
            body.InstallEngine(engine);
    
            // 安装座椅
            Seat seat = new Seat();
            body.InstallSeat(seat);
    
            // 安装车轮
            Wheel wheel = new Wheel();
            body.InstallWheel(wheel);
    
            // 安装音响系统
            AudioSystem audioSystem = new AudioSystem();
            body.InstallAudioSystem(audioSystem);
    
            return new Sedan(body, engine, seat, wheel, audioSystem);
        }
    }
    
    public class Sedan
    {
        public Body Body { get; }
        public Engine Engine { get; }
        public Seat Seat { get; }
        public Wheel Wheel { get; }
        public AudioSystem AudioSystem { get; }
    
        public Sedan(Body body, Engine engine, Seat seat, Wheel wheel, AudioSystem audioSystem)
        {
            Body = body;
            Engine = engine;
            Seat = seat;
            Wheel = wheel;
            AudioSystem = audioSystem;
        }
    
        public void Drive()
        {
            Console.WriteLine("Driving a Sedan");
        }
    }
    
    // 具体的车身、引擎、座椅、车轮、音响系统类的定义
    

    在这个示例中,轿车的创建过程包括了多个步骤,例如创建车身、安装引擎、安装座椅、安装车轮和安装音响系统。在轿车工厂类中,我们按照这个制造流程创建了一个完整的轿车对象,并返回给客户端使用。由于制造流程比较复杂,使用工厂模式可以将这个复杂的过程封装起来,客户端代码只需要调用工厂方法即可创建对象,无需了解具体的创建细节。

  • 解析Rss和Atom订阅

    想着闲的没事的时候重构一下朋友圈的项目,打算改成前后端分离的架构,前端使用Blazor webassembly 后端使用ASP.NET Core Web Api。先写一个简单的Rss和atom订阅解析功能~

    using System.Xml;
    using Moment.Shared.DBModel;
    
    namespace Moment.Core;
    
    /// <summary>
    /// 核心模块
    /// </summary>
    public static class Core
    {
        /// <summary>
        /// 获取Atom的订阅内容
        /// </summary>
        /// <param name="url">链接</param>
        /// <returns></returns>
        public static async Task<List<FeedItem>> Atom(string url)
        {
            var feedItems = new List<FeedItem>();
            var httpClient = new HttpClient();
            var feed = await httpClient.GetStringAsync(url);
            var xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(feed);
            var nsManager = new XmlNamespaceManager(xmlDoc.NameTable);
            nsManager.AddNamespace("atom", "http://www.w3.org/2005/Atom");
            var entries = xmlDoc.SelectNodes("//atom:entry", nsManager);
            foreach (XmlNode entry in entries!)
            {
                var title = entry.SelectSingleNode("atom:title", nsManager)?.InnerText;
                var link = entry.SelectSingleNode("atom:link", nsManager)?.Attributes!["href"]!.Value;
                var summary = entry.SelectSingleNode("atom:summary", nsManager)?.InnerText;
                var content = entry.SelectSingleNode("atom:content", nsManager)?.InnerText;
                var published = entry.SelectSingleNode("atom:published", nsManager)?.InnerText;
                feedItems.Add(new FeedItem
                {
                    Title = title, Link = link, Content = content, Summary = summary, Published = published
                });
            }
    
            return feedItems;
        }
    
        /// <summary>
        /// 获取Rss2.0的订阅内容
        /// </summary>
        /// <param name="url">链接</param>
        /// <returns></returns>
        public static async Task<List<FeedItem>> Rss(string url)
        {
            var feedItems = new List<FeedItem>();
            var httpClient = new HttpClient();
            var feed = await httpClient.GetStringAsync(url);
            var xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(feed);
            var nsManager = new XmlNamespaceManager(xmlDoc.NameTable);
            nsManager.AddNamespace("rss", "http://rss2.org/schema/");
            nsManager.AddNamespace("content", "http://purl.org/rss/1.0/modules/content/");
            var entries = xmlDoc.SelectNodes("//rss/channel/item", nsManager);
            foreach (XmlNode entry in entries!)
            {
                var title = entry.SelectSingleNode("./title", nsManager)?.InnerText;
                var link = entry.SelectSingleNode("./link", nsManager)?.InnerText;
                var summary = entry.SelectSingleNode("./description", nsManager)?.InnerText;
                var content = entry.SelectSingleNode("./content:encoded", nsManager)?.InnerText;
                var published = entry.SelectSingleNode("./pubDate", nsManager)?.InnerText; // Added line to ge
                feedItems.Add(new FeedItem
                {
                    Title = title, Link = link, Content = content, Summary = summary, Published = published
                });
            }
    
            return feedItems;
        }
    }
  • C# WebApi的JWT示例

    最近写了一个小工具用了JWT作为验证,下面是一个简单的JWT Demo,包含用户注册和登录获取Token

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.IdentityModel.Tokens;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using System.Text;
    using Moment.Shared;
    using Moment.Shared.DBModel;
    
    namespace Moment.Core.Controllers;
    
    /// <summary>
    /// JWT令牌授权控制器
    /// </summary>
    [Route("[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private readonly IConfiguration _configuration;
        private readonly IFreeSql _db;
    
        public AuthController(IConfiguration configuration, IFreeSql db)
        {
            _configuration = configuration;
            _db = db;
        }
    
        /// <summary>
        /// 注册用户
        /// </summary>
        /// <param name="email">用户邮箱</param>
        /// <param name="password">用户密码</param>
        /// <returns></returns>
        [HttpPost("Register")]
        public async Task<ActionResult<Result>> Register(string email, string password)
        {
            // 检查邮箱是否存在
            var item = await _db.Select<User>().Where(x => x.Email == email).FirstAsync();
            if (item is not null)
            {
                return BadRequest(new Result("邮箱已经注册!"));
            }
    
            // 注册用户
            var user = new User
            {
                Email = email,
                PasswordHash = BCrypt.Net.BCrypt.HashPassword(password)
            };
            await _db.Insert<User>().AppendData(user).ExecuteAffrowsAsync();
            return Ok(new Result(msg: "注册成功!", content: user));
        }
    
        /// <summary>
        /// 用户登录获取Token
        /// </summary>
        /// <param name="email">用户邮箱</param>
        /// <param name="password">用户密码</param>
        /// <returns>Token值</returns>
        [HttpPost("Login")]
        public async Task<ActionResult<User>> Login(string email, string password)
        {
            var user = await _db.Select<User>().Where(x => x.Email == email).FirstAsync();
            // 检查用户存在
            if (user is null)
                return BadRequest(new Result("用户不存在!"));
            var res = BCrypt.Net.BCrypt.Verify(password, user.PasswordHash);
            // 检查密码正确
            if (!res)
                return BadRequest(new Result("密码错误!"));
            return Ok(new Result("登录成功!", CreateToken(user)));
        }
    
        private string CreateToken(User user)
        {
            List<Claim> claims = new List<Claim>
            {
                new Claim(ClaimTypes.Email, user.Email),
                new Claim(ClaimTypes.Role, user.Role)
            };
    
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
                _configuration.GetSection("AppSettings:Token").Value!));
    
            var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
    
            var token = new JwtSecurityToken(
                claims: claims,
                expires: DateTime.Now.AddDays(1),
                signingCredentials: cred
            );
    
            var jwt = new JwtSecurityTokenHandler().WriteToken(token);
            return jwt;
        }
    }