.Net6 Api Swagger配置

1、定义个Swagger版本(组)的枚举

namespace WebApp.Enums
{
    /// <summary>
    /// api版本枚举
    /// </summary>
    public enum ApiVersion
    {
        /// <summary>
        /// v1版本
        /// </summary>
        v1 = 1,
        /// <summary>
        /// v2版本
        /// </summary>
        v2 = 2,
    }
}

2、添加SwaggerExtentsion扩展类,配置注册Swagger

using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Net.Http.Headers;
using System.Net;
using System.Reflection;
using System.Text;
using WebApp.Enums;

namespace WebApp.Common.Swagger
{
    /// <summary>
    /// SwaggerDoc配置
    /// 用法:在Program.cs文件中进行注册:builder.AddSwaggerGenExt();
    /// </summary>
    public static class SwaggerExtension
    {
        /// <summary>
        /// 扩展方法:Swagger文档
        /// </summary>
        /// <param name="services"></param>
        public static void AddSwaggerGenExt(this IServiceCollection services)
        {
            #region 添加Swagger
            //获取的是当前执行的方法所在的程序文件的名称:即项目名称,例如我的项目名称叫webapp 
            var AssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
            services.AddSwaggerGen(options =>
            {
                #region 配置版本
                //options.SwaggerDoc("v1", new OpenApiInfo { Title = "售楼API", Version = "v1" });
                typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
                {
                    options.SwaggerDoc(version, new OpenApiInfo()
                    {
                        Version = version,
                        Title = "凤凰网管理系统",
                        Description = $"凤凰网接口服务    版本: {version}",
                        Contact = new OpenApiContact
                        {
                            Name = "潇湘夜雨",
                            Email = "123@qq.com"
                        }
                    });
                });

                #endregion
                #region 配置注释文档

                // 获取当前项目的 XML文档文件路径:比如我的项目名称叫WebApp,那么它默认的 XML文档文件路径就是当前项目下的 WebApp.xml
                var xmlFile = $"{AssemblyName}.xml";
                var xmlFileFullPath = Path.Combine(AppContext.BaseDirectory, xmlFile);

                //var domainXmlPath = Path.Combine(AppContext.BaseDirectory, "Bgy.Domain.xml");      // 获取Bgy.Domain.xml文件路径
                //var viewmodelXmlPath = Path.Combine(AppContext.BaseDirectory, "Bgy.ViewModel.xml");// 获取Bgy.ViewModel.xml文件路径

                options.IncludeXmlComments(xmlFileFullPath, true); // 添加控制器层注释,true表示显示控制器注释

                //options.IncludeXmlComments(domainXmlPath);       // 添加Domain层注释
                //options.IncludeXmlComments(viewmodelXmlPath);    // 添加ViewModel层注释

                //对action的名称进行排序。
                options.OrderActionsBy(o => o.RelativePath);

                #endregion

                #region 配置授权认证信息

                //添加一个必须的全局安全信息,
                //第一个参数是方案唯一名称:和AddSecurityDefinition方法指定的方案名称标识一致即可:BearerAuth
                //第二个参数是方案的描述:可以是BasicAuthScheme、ApiKeyScheme的实例或OAuth2Scheme
                options.AddSecurityDefinition("BearerAuth", new OpenApiSecurityScheme()
                {
                    Description = "在下框中输入请求头中需要添加Jwt授权Token:Bearer Token",
                    Name = "Authorization",
                    In = ParameterLocation.Header, //配置jwt默认加在Authorization信息的位置:这里配置的是将jwt信息放在请求头Header中            
                    Type = SecuritySchemeType.Http,//使用Authorize头部                  
                    Scheme = "bearer", //内容为以 bearer开头
                    BearerFormat = "JWT",
                    //Reference= new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "bearerAuth" }
                });

                //注册全局认证(所有的接口都可以使用认证)
                options.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "BearerAuth" //方案名称标识
                            }
                        },
                        new string[] {} //不设权限
                    }
                });

                #endregion

                #region 在Swagger中扩展文件上传按钮
                options.OperationFilter<FileUploadFilter>();
                #endregion
            });
            #endregion
        }

        /// <summary>
        /// 扩展方法:配置SwaggerUI
        /// 用法:在Program.cs文件中进行注册:app.UseSwaggerUIExt();
        /// </summary>
        /// <param name="app"></param>
        public static void UseSwaggerUIExt(this WebApplication app)
        {
            //SwaggerBasicAuthMiddleware:是我自己扩展的一个中间件:目的是需要登陆才能到达Swagger的Index页面中,否则无法进入:可以根据需要去掉这个
            //需要安装:Swashbuckle.AspNetCore包
            //app.UseMiddleware<SwaggerBasicAuthMiddleware>();

            if (app.Environment.IsDevelopment())
            {
                //app.UseSwagger();
                //app.UseSwaggerUI();
            }

            var enviroment = app.Configuration["Swagger:environmentVariables"];
            switch (enviroment)
            {
                case "development":
                    app.UseSwagger();//启用Swagger中间件

                    app.UseSwaggerUI(options =>  //配置版本
                    {
                        typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
                        {
                            options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version);
                        });

                    });

                    break;
                case "testing":
                    app.UseSwagger();
                    app.UseSwaggerUI(options =>
                    {
                        typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
                        {
                            options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version);
                        });
                    });
                    break;
                case "production":
                    break;

            }
        }
    }

    #region 扩展功能
    /// <summary>
    /// 文件上传的扩展:实现在Swagger中上传文件的功能
    /// </summary>
    public class FileUploadFilter : IOperationFilter
    {
        /// <summary>
        /// 文件上传筛选:只有上传文件的方法才添加此功能
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="context"></param>
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            const string FileUploadContentType = "multipart/form-data";
            if (operation.RequestBody == null || !operation.RequestBody.Content.Any(x => x.Key.Equals(FileUploadContentType, StringComparison.InvariantCultureIgnoreCase)))
            {
                return;
            }
            if (context.ApiDescription.ParameterDescriptions[0].Type == typeof(IFormCollection))
            {
                operation.RequestBody = new OpenApiRequestBody
                {
                    Description = "文件上传",
                    Content = new Dictionary<string, OpenApiMediaType>
                    {
                        {
                            FileUploadContentType,new OpenApiMediaType
                            {
                                Schema=new OpenApiSchema
                                {
                                    Type="object",
                                    Required=new HashSet<string>{ "file"},
                                    Properties=new Dictionary<string, OpenApiSchema>
                                    {
                                        {
                                            "file",new OpenApiSchema
                                            {
                                                Type="string",
                                                Format="binary"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                };
            }
        }
    }

    /// <summary>
    /// 如何在ASP.Net Core的生产环境中保护swagger ui,也就是index.html页面。其实swagger是自带禁用的功能的,只需要设置开关即可。
    /// 但是有一些场景,是需要把这些接口进行开放或者导出成文档供第三方进行调用,这个时候却又不想让所有人访问。
    /// 这里介绍一种权限控制访问的方式,用来指定用户使用;
    /// </summary>
    public class SwaggerBasicAuthMiddleware
    {
        private readonly RequestDelegate next;
        /// <summary>
        /// 增加对swagger ui的验证
        /// </summary>
        /// <param name="next"></param>
        public SwaggerBasicAuthMiddleware(RequestDelegate next)
        {
            this.next = next;
        }
        /// <summary>
        /// 登陆功能实现
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context)
        {
            if (context.Request.Path.StartsWithSegments("/swagger"))
            {
                string authHeader = context.Request.Headers["Authorization"];
                if (authHeader != null && authHeader.StartsWith("Basic "))
                {
                    // Get the credentials from request header
                    var header = AuthenticationHeaderValue.Parse(authHeader);
                    var inBytes = Convert.FromBase64String(header.Parameter);
                    var credentials = Encoding.UTF8.GetString(inBytes).Split(':');
                    var username = credentials[0];
                    var password = credentials[1];

                    //用户身份认证
                    if (username.Equals("admin") && password.Equals("123456"))
                    {
                        await next.Invoke(context).ConfigureAwait(false);
                        return;
                    }
                }
                context.Response.Headers["WWW-Authenticate"] = "Basic";
                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            }
            else
            {
                await next.Invoke(context).ConfigureAwait(false);
            }
        }
    }
    #endregion
}

3、appsettings.json配置文件

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Swagger": {
    "environmentVariables": "development" //:development  :testing  :production
  }
}

4、在Program.cs中注册SwaggerDoc及启用SwaggerUI

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using WebApp.Common.Swagger;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddEndpointsApiExplorer();


#region JWT鉴权授权
var audience = "Audience";
var issuer = "Issuer";
var securityKey = "SIGfMA0FCSqGSIb3DFEBAQUAA4GNADCBiQKBgQDI2a2EJ7d872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI593nNDAPfnJsas96mSA9Q/mD8RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB";
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)  //默认授权机制名称;                                      
         .AddJwtBearer(options =>
         {
             options.TokenValidationParameters = new TokenValidationParameters
             {
                 ValidateIssuer = true,//是否验证Issuer
                 ValidateAudience = true,//是否验证Audience
                 ValidateLifetime = true,//是否验证失效时间
                 ValidateIssuerSigningKey = true,//是否验证SecurityKey
                 ValidAudience = audience,//Audience
                 ValidIssuer = issuer,//Issuer,这两项和前面签发jwt的设置一致  表示谁签发的Token
                 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey))//拿到SecurityKey
             };
         });
#endregion


builder.Services.AddSwaggerGenExt();//SwaggerGen

var app = builder.Build();

app.UseSwaggerUIExt(); //SwaggerUI

app.UseAuthentication();

app.UseAuthorization();

app.MapControllers();

app.Run();

5、Api接口中使用

在接口控制器,或者方法上添加版本(组)标识:[ApiExplorerSettings(GroupName = "v1")]

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using WebApp.Enums;

namespace WebApp.Controllers
{
    /// <summary>
    /// 测试接口
    /// </summary>
    [ApiController]//[ApiController]能够推断参数的绑定源,就不需要[FromBody][FromForm][FromHeader][FromQuery][FromRoute]....来主动指定接收参数的形式
    [Route("api/[controller]/[action]")]
  
    public class HomeController : ControllerBase
    {
        private readonly ILogger<HomeController> _logger;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger"></param>
        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }


        /// <summary>
        /// 查询案列1
        /// </summary>
        /// <param name="id">编号</param>
        /// <returns></returns>
        [HttpGet("Abc")] //url地址是:api/WeatherForecast/Get/Abc
        [ApiExplorerSettings(GroupName = "v1")]
        [Authorize]
        public IActionResult Get(int id)
        {
            return Ok(id);

            //返回值:IActionResult

            //return NotFound(); 404

            //return Redirect("/Home/Index");

            //var content = "Hello, World!";
            //return Content(content, "text/plain");

            //var data = new { Name = "John", Age = 30 };
            //return Json(data);

            //var filePath = "/path/to/file.pdf";
            //return File(filePath, "application/pdf", "filename.pdf");

            //byte[] videoBytes = System.IO.File.ReadAllBytes(containerPath);
            //return File(videoBytes, "video/mp4");
        }

        /// <summary>
        /// 查询案列2:路由的伪静态
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [HttpGet("Abc/{name}")] //url地址是:api/WeatherForecast/Get/Abc/lily   :lily是name值, 同时name值是必填的,{name}必须要与action的参数名称一致。这就是路由的伪静态形式
        [ApiExplorerSettings(GroupName = nameof(ApiVersion.v1))]
        [Authorize]
        public IActionResult Get(string name)
        {
            return Ok(name);
        }


        /// <summary>
        /// 客户端登陆
        /// </summary>
        /// <param name="clientid">客户端名称</param>
        /// <param name="password">客户端密码</param>
        /// <returns>返回jwtToken</returns>
        [HttpGet]
        [ApiExplorerSettings(GroupName = nameof(ApiVersion.v2))]
        [Route("api/login")]
        public IActionResult Login(string clientid, string password)
        {
            //这里肯定是需要去连接数据库做数据校验
            if (clientid == "admin" && password == "123456")//应该数据库
            {
                string token = GetJwtToken(clientid);
                return Ok(new { token });
            }
            else
            {
                return Ok("");
            }
        }

        /// <summary>
        /// 获取Token
        /// </summary>
        /// <param name="UserName"></param>
        /// <returns></returns>
        [NonAction]
        public string GetJwtToken(string UserName)
        {
            var issuer = "Issuer";
            var audience = "Audience";

            var securityKey = "SIGfMA0FCSqGSIb3DFEBAQUAA4GNADCBiQKBgQDI2a2EJ7d872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI593nNDAPfnJsas96mSA9Q/mD8RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB";

            Claim[] claims = new[]
            {
               new Claim(ClaimTypes.Name, UserName)
            };
            SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(securityKey));
            SigningCredentials creds = new(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: issuer,
                audience: audience,
                claims: claims,
                expires: DateTime.Now.AddMinutes(1),//5分钟有效期
                signingCredentials: creds);
            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        /// <summary>
        /// 文件上传
        /// </summary>
        /// <param name="from"></param>
        /// <returns></returns>
        [HttpPost]
        public JsonResult UploadFile(IFormCollection from)
        {
            return new JsonResult(new
            {
                Success = true,
                Message = "上传成功",
                FileName = from.Files.FirstOrDefault()?.FileName

            }) ;     
        }


        /// <summary>
        /// 标记了[NonAction]特性,则不被视为控制器的操作方法
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost(Name = "{id}")]
        [NonAction]
        public string PostTest(int id)
        {
            return id.ToString();
        }
    }
}

6、项目配置生成XML文件

7、效果图

7、注意点:

如果只是单纯只返回token的时候,记得在控制器右上角的Authorize里  先写Bearer+空格+你的token 

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

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

相关文章

深度学习之基于Django+Tensorflow动物识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于Django和TensorFlow的动物识别系统可以被设计成能够使用深度学习算法自动识别上传的图像中的动物种类&#xff…

【AICFD案例教程】多孔介质歧管流动传热

AICFD是由天洑软件自主研发的通用智能热流体仿真软件&#xff0c;用于高效解决能源动力、船舶海洋、电子设备和车辆运载等领域复杂的流动和传热问题。软件涵盖了从建模、仿真到结果处理完整仿真分析流程&#xff0c;帮助工业企业建立设计、仿真和优化相结合的一体化流程&#x…

Linux C 网络编程概述

网络编程 计算机网络概述分类网络体系结构通信协议通信流程网络通信帧格式以太网帧格式分析ARP 协议分析IP 数据报分析IP分类IP 分配子网掩码 TCP 段分析 TCP三次握手协议 ⭐TCP四次挥手协议 ⭐ TCP编程基于 TCP 客户端编程-步骤说明基于 TCP 服务器端编程-步骤说明基于 TCP 服…

中间件安全:Apache Tomcat 弱口令.(反弹 shell 拿到服务器的最高控制权.)

中间件安全&#xff1a;Apache Tomcat 弱口令. Tomcat 是 Apache 软件基金会&#xff08;Apache Software Foundation&#xff09;的 Jakarta 项目中的一个核心项目&#xff0c;由 Apache、Sun 和其他一些公司及个人共同开发而成。 通过弱口令登录后台&#xff0c;部署 war 包…

【狂神说Java】redis

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;【狂神说Java】 &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0c…

Linux02 VIM编辑器

Linux02 VIM编辑器 基本上 vi/vim 共分为三种模式&#xff0c; 分别是命令模式&#xff08;Command mode&#xff09;&#xff0c;输入模式&#xff08;Insert mode&#xff09;和底线命令模式&#xff08;Last line mode&#xff09;。 三种状态进行切换 插入模式&#xff1a…

如何优雅的避免空指针异常

文章目录 1.数据准备2.实战&#xff1a;获取用户所在的城市2.1.直接获取&#xff1b;容易出现空指针异常。2.2.使用if-else判断&#xff1b;避免了出现空指针的问题&#xff0c;但是代码结构层次嵌套多&#xff0c;不美观2.3.使用工具类美化一下if判断代码2.4.使用Optional解决…

html实现计算器源码

文章目录 1.设计来源1.1 主界面1.2 计算效果界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134532725 html实现计算器源码&#xff0c;计算器源码&#xff0c;简易计…

2022最新版-李宏毅机器学习深度学习课程-P51 BERT的各种变体

之前讲的是如何进行fine-tune&#xff0c;现在讲解如何进行pre-train&#xff0c;如何得到一个pre train好的模型。 CoVe 其实最早的跟预训练有关的模型&#xff0c;应该是CoVe&#xff0c;是一个基于翻译任务的一个模型&#xff0c;其用encoder的模块做预训练。 但是CoVe需要…

CV计算机视觉每日开源代码Paper with code速览-2023.11.17

点击CV计算机视觉&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【点云分割】&#xff08;CVPR2023&#xff09;Center Focusing Network for Real-Time LiDAR Panoptic Segmentation 论文地址&#xff1a;…

【每日OJ —— 232.用栈实现队列(栈)】

每日OJ —— 232.用栈实现队列&#xff08;栈&#xff09; 1.题目&#xff1a;232.用栈实现队列&#xff08;栈&#xff09;2.解法2.1.方法讲解2.1.1.算法讲解2.1.2.代码实现2.1.3.提交通过展示 1.题目&#xff1a;232.用栈实现队列&#xff08;栈&#xff09; 2.解法 2.1.方法…

图解算法数据结构-LeetBook-栈和队列04_望远镜中最高的海拔_滑动窗口

科技馆内有一台虚拟观景望远镜&#xff0c;它可以用来观测特定纬度地区的地形情况。该纬度的海拔数据记于数组 heights &#xff0c;其中 heights[i] 表示对应位置的海拔高度。请找出并返回望远镜视野范围 limit 内&#xff0c;可以观测到的最高海拔值。 示例 1&#xff1a; 输…

AI技术实力认证,宏电股份荣获2023年度AI天马“领军企业”

近日&#xff0c;由中国新一代人工智能发展战略研究院指导&#xff0c;深圳市人工智能产业协会主办&#xff0c;广东未来产业研究院承办的2023年度“AI天马”认定最终结果公布&#xff0c;宏电股份荣获AI天马“领军企业”奖项。 宏电股份基于20余年的技术沉淀&#xff0c;在工业…

【OpenGauss 列存储学习总结 2】

OpenGauss 列存储学习总结 2 概述文章链接 概述 列存储是一种优化技术&#xff0c;用于在数据库系统中存储和查询大量数据。与传统的行存储方式不同&#xff0c;列存储将每个列的数据分别存储在独立的存储单元中&#xff0c;而不是按照行的方式存储。这种存储方式在分析性查询、…

Java GUI实现桌球小游戏

桌球游戏是一种室内运动&#xff0c;通常在一个正式的桌球台上进行。这种游戏也被称为台球或母球。桌球游戏的目标是使用一个击球杆将彩球击入桌面四个角落的袋子中&#xff0c;得分最高的一方获胜。桌球游戏需要一定的技巧和策略&#xff0c;因此是一项受欢迎的竞技运动和休闲…

Vue:[##################] / reify:core-js: timing reifyNode:node_modules/lodash Completed in 4923ms

Vue创建项目卡在[##################] / reify:core-js: timing reifyNode:node_modules/lodash Completed in 4923ms不动的问题. 遇到问题不要慌&#xff0c;别人可以你也可以。 1.什么是npm npm是node官方的包管理器。 cnpm是个中国版的npm&#xff0c;是淘宝定制的 cnpm (g…

第2关:图的深度优先遍历

任务要求参考答案评论2 任务描述相关知识编程要求测试说明 任务描述 本关任务&#xff1a;以邻接矩阵存储图&#xff0c;要求编写程序实现图的深度优先遍历。 相关知识 图的深度优先遍历类似于树的先序遍历, 是树的先序遍历的推广&#xff0c;其基本思想如下&#xff1a; …

广西梧州盾构机主轴承尺寸测量检测CAV检测上门三维扫描-CASAIM中科广电

一、背景介绍 大型盾构机在掘进过程中&#xff0c;只能前进&#xff0c;不能倒退&#xff0c;盾构机掘进过程中&#xff0c;主轴承“手持”刀盘旋转切削掌子面并为刀盘提供旋转支撑&#xff0c;主轴承一旦失效&#xff0c;会造成严重损失。因此&#xff0c;主轴承是盾构机刀盘…

软件系统运维方案

1.项目情况 2.服务简述 2.1服务内容 2.2服务方式 2.3服务要求 2.4服务流程 2.5工作流程 2.6业务关系 2.7培训 3.资源提供 3.1项目组成员 3.2服务保障 点击获取所有软件开发资料&#xff1a;点我获取

PHP中cookie与session使用指南

PHP中cookie与session使用指南 Cookie和session的出现&#xff0c;是为了解决http协议无状态交互的窘境&#xff0c;它们都用于存储客户端的相关信息 0x01 Cookie使用 简介 Cookie 是一种在客户端存储数据的机制&#xff0c;通常用于记录用户的状态和偏好。下面将介绍如何在…