<sup id="sjju3"><noscript id="sjju3"></noscript></sup>
    <big id="sjju3"></big>

  • <blockquote id="sjju3"></blockquote>
    <blockquote id="sjju3"></blockquote>

      <td id="sjju3"></td>

      <big id="sjju3"></big>
        <code id="sjju3"><strong id="sjju3"><dl id="sjju3"></dl></strong></code>
      1. Asp.Net Core基于JWT认证的数据接口网关实例代码

         更新时间:2019年03月08日 10:49:03   作者:如兹   我要评论

        这篇文章主要给大家介绍了关于Asp.Net Core基于JWT认证的数据接口网关的相关资料,文中通过示例代码以及图文介绍的非常详细,对大家的学习或者使用Asp.net Core具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

        前言

        近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo。朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对前后端分离的数据服务支持,于是想到我一直做.Net开发,问我是否对.Net Core有所了解?能不能做个简单Demo出来看看?我说,?#20540;?#25196;镳之后我不是调用别人的接口就是提供接口给别?#35828;?#29992;,于是便有了以下示例代码。

        示例要求能演示获取Token及如何使用该Token访问数据资源,在Demo中实现了JWT的颁发及验证以及重写一个ActionAuthorizeAttribute实现对具体数据接口的调用权限控制,先看一下项目截图:

        [项目截图]

        项目文件介绍

        解决方案下只有一个项目,项目名称就叫Jwt.Gateway,包含主要文件有:

        1. Controllers目录下的ApiActionFilterAttribute.cs文件,继承Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute,用于校验接口调用者对具体接口的访问权限。
        2. Controllers目录下的ApiBase.cs文件,继承Microsoft.AspNetCore.Mvc.Controller,具有Microsoft.AspNetCore.Authorization.Authorize特性引用,用于让所有数据接口用途的控制器继承,定义有CurrentAppKey属性(来访应用程序的身份标识)并在OnActionExecuting事件中统一分析Claims并赋值。
        3. Controllers目录下的TokenController.cs控制器文件,用于对调用?#25509;?#29992;程序获取及注销Token。
        4. Controllers目录下的UsersController.cs控制器文件,继承ApiBase.cs,作为数据调用示例。
        5. MiddleWares目录下的ApiCustomException.cs文件,是一个数据接口的统一异常处理中间件。
        6. Models目录下的ApiResponse.cs文件,用于做数据接口的统一数据及错误信息输出实体模型。
        7. Models目录下的User.cs文件,示例数据实体模型。
        8. Program.cs及Startup.cs文件就不介绍了,随便建个空项目都有。

        项目文件代码

        ApiActionFilterAttribute.cs

        Controllers目录下的ApiActionFilterAttribute.cs文件,继承Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute,用于校验接口调用者对具体接口的访问权限。

        设想每一个到访的请求都是一个应用程序,每一个应用程序都分配有基本的Key和Password,每一个应用程序具有不同的接口访问权限,所以在具体的数据接口上应该声明该接口所要求的权限值,比如修改用户信息的接口应该在接口方法上声明需要具有“修改用户”的权限,用例: [ApiActionFilter("用户修改")]

        大部分情况下一个接口(方法)对应一个操作,这样基本上就能应付了,但是不排除有时候可能需要多个权限组合进行验证,所以该文件中有一个对多个权限值进行校验的“与”和“和”枚举,用例: [ApiActionFilter(new string[] { "用户修改", "用户录入", "用户删除" },ApiActionFilterAttributeOption.AND)] ,这样好像就差不多了。

        由于在一个接口调用之后可能需要将该接口所声明需要的权限值记入日志等需求,因此权限值集合将被写入到HttpContext.Items["Permissions"]中以方便可能的后续操作访问,看代码:

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore.Mvc.Filters;
        
        namespace Jwt.Gateway.Controllers
        {
         public enum ApiActionFilterAttributeOption
         {
         OR,AND
         }
         public class ApiActionFilterAttribute : Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute
         {
         List<string> Permissions = new List<string>();
         ApiActionFilterAttributeOption Option = ApiActionFilterAttributeOption.AND;
         public ApiActionFilterAttribute(string permission)
         {
         Permissions.Add(permission);
         }
         public ApiActionFilterAttribute(string[] permissions, ApiActionFilterAttributeOption option)
         {
         foreach(var permission in permissions) {
         if (Permissions.Contains(permission))
         {
          continue;
         }
         Permissions.Add(permission);
         }
         Option = option;
         }
        
         public override void OnActionExecuting(ActionExecutingContext context)
         {
         var key = GetAppKey(context);
         List<string> keyPermissions = GetAppKeyPermissions(key);
         var isAnd = Option == ApiActionFilterAttributeOption.AND;
         var permissionsCount = Permissions.Count;
         var keyPermissionsCount = keyPermissions.Count;
         for (var i = 0; i < permissionsCount; i++)
         {
         bool flag = false;
         for (var j = 0; j < keyPermissions.Count; j++)
         {
          if (flag = string.Equals(Permissions[i], keyPermissions[j], StringComparison.OrdinalIgnoreCase))
          {
          break;
          }
         }
         if (flag)
         {
          continue;
         }
         if (isAnd)
         {
          throw new Exception("应用“" + key + "?#27604;?#23569;“" + Permissions[i] + "”的权限");
         }
         }
        
         context.HttpContext.Items.Add("Permissions", Permissions);
        
         base.OnActionExecuting(context);
         }
        
         private string GetAppKey(ActionExecutingContext context)
         {
         var claims = context.HttpContext.User.Claims;
         if (claims == null)
         {
         throw new Exception("未能获取?#25509;?#29992;标识");
         }
         var claimKey = claims.ToList().Find(o => string.Equals(o.Type, "AppKey", StringComparison.OrdinalIgnoreCase));
         if (claimKey == null)
         {
         throw new Exception("未能获取?#25509;?#29992;标识");
         }
        
         return claimKey.Value;
         }
         private List<string> GetAppKeyPermissions(string appKey)
         {
         List<string> li = new List<string>
         {
         "用户明细","用户列表","用户录入","用户修改","用户删除"
         };
         return li;
         }
        
         }
        }
        
        ApiActionAuthorizeAttribute.cs

        ApiBase.cs

        Controllers目录下的ApiBase.cs文件,继承Microsoft.AspNetCore.Mvc.Controller,具有Microsoft.AspNetCore.Authorization.Authorize特性引用,用于让所有数据接口用途的控制器继承,定义有CurrentAppKey属性(来访应用程序的身份标识)并在OnActionExecuting事件中统一分析Claims并赋值。

        通过验证之后,Aps.Net Core会在HttpContext.User.Claims中将将来访者的身份信息记录下来,我们可以通过该集?#31995;?#21040;来访者的身份信息。

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore.Http;
        using Microsoft.AspNetCore.Mvc;
        using Microsoft.AspNetCore.Mvc.Filters;
        
        namespace Jwt.Gateway.Controllers
        {
         [Microsoft.AspNetCore.Authorization.Authorize]
         public class ApiBase : Microsoft.AspNetCore.Mvc.Controller
         {
         private string _CurrentAppKey = "";
         public string CurrentAppKey { get { return _CurrentAppKey; } }
         public override void OnActionExecuting(ActionExecutingContext context)
         {
          var claims = context.HttpContext.User.Claims.ToList();
          var claim = claims.Find(o => o.Type == "appKey");
          if (claim == null)
          {
          throw new Exception("未通过认证");
          }
          var appKey = claim.Value;
          if (string.IsNullOrEmpty(appKey))
          {
          throw new Exception("appKey不合法");
          }
        
          _CurrentAppKey = appKey;
        
          base.OnActionExecuting(context);
         }
         }
        }
        
        ApiBase.cs

        TokenController.cs

        Controllers目录下的TokenController.cs控制器文件,用于对调用?#25509;?#29992;程序获取及注销Token。

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore.Http;
        using Microsoft.AspNetCore.Mvc;
        
        namespace Jwt.Gateway.Controllers
        {
         [Route("api/[controller]/[action]")]
         public class TokenController : Controller
         {
         private readonly Microsoft.Extensions.Configuration.IConfiguration _configuration;
        
         public TokenController(Microsoft.Extensions.Configuration.IConfiguration configuration)
         {
          _configuration = configuration;
         }
        
         // /api/token/get
         public IActionResult Get(string appKey, string appPassword)
         {
          try
          {
          if (string.IsNullOrEmpty(appKey))
          {
           throw new Exception("缺少appKey");
          }
          if (string.IsNullOrEmpty(appKey))
          {
           throw new Exception("缺少appPassword");
          }
          if (appKey != "myKey" && appPassword != "myPassword")//固定的appKey及appPassword,实际项目中应该来自数据库或配置文件
          {
           throw new Exception("配置不存在");
          }
        
          var key = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_configuration["JwtSecurityKey"]));
          var creds = new Microsoft.IdentityModel.Tokens.SigningCredentials(key, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256);
          var claims = new List<System.Security.Claims.Claim>();
          claims.Add(new System.Security.Claims.Claim("appKey", appKey));//仅在Token中记录appKey
          var token = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(
           issuer: _configuration["JwtTokenIssuer"],
           audience: _configuration["JwtTokenAudience"],
           claims: claims,
           expires: DateTime.Now.AddMinutes(30),
           signingCredentials: creds);
        
          return Ok(new Models.ApiResponse { status = 1, message = "OK", data = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler().WriteToken(token) });
        
          }
          catch(Exception ex)
          {
          return Ok(new Models.ApiResponse { status = 0, message = ex.Message, data = "" });
          }
         }
         
         // /api/token/delete
         public IActionResult Delete(string token)
         {
          //code: 加入黑名单,使其无效
        
          return Ok(new Models.ApiResponse { status = 1, message = "OK", data = "" });
         }
        
        
         }
        }
        
        TokenController.cs

        UsersController.cs

        Controllers目录下的UsersController.cs控制器文件,继承ApiBase.cs,作为数据调用示例。

        该控制器定义了对User对象常规的明细、列表、录入、修改、删除等操作。

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore.Mvc;
        
        namespace Jwt.Gateway.Controllers
        {
         [Produces("application/json")]
         [Route("api/[controller]/[action]")]
         public class UsersController : ApiBase
         {
         /* 
          * 1.要访问访问该控制器提供的接口请先通过"/api/token/get"获取token
          * 2.访问该控制器提供的接口http请求头必须具有值为"Bearer+空格+token"的Authorization键,格式参考:
          * "Authorization"="Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQXBwIiwiYXBwS2V5IjoibXlLZXkiLCJleHAiOjE1NTE3ODc2MDMsImlzcyI6IkdhdGV3YXkiLCJhdWQiOiJhdWRpZW5jZSJ9.gQ9_Q7HUT31oFyfl533T-bNO5IWD2drl0NmD1JwQkMI"
         */
        
         /// <summary>
         /// 临时用户测试数据,实际项目中应该来自数据库等媒介
         /// </summary>
         static List<Models.User> _Users = null;
         static object _Lock = new object();
         public UsersController()
         {
          if (_Users == null)
          {
          lock (_Lock)
          {
           if (_Users == null)
           {
           _Users = new List<Models.User>();
           var now = DateTime.Now;
           for(var i = 0; i < 10; i++)
           {
            var num = i + 1;
            _Users.Add(new Models.User { UserId = num, UserName = "name"+num, UserPassword = "pwd"+num, UserJoinTime = now });
           }
           }
          }
          }
         }
        
         // /api/users/detail
         [ApiActionFilter("用户明细")]
         public IActionResult Detail(long userId)
         {
          /*
          //获取appKey(在ApiBase中写入)
          var appKey = CurrentAppKey;
          //获取使用的权限(在ApiActionAuthorizeAttribute中写入)
          var permissions = HttpContext.Items["Permissions"];
          */
        
          var user = _Users.Find(o => o.UserId == userId);
          if (user == null)
          {
          throw new Exception("用户不存在");
          }
        
          return Ok(new Models.ApiResponse { data = user, status = 1, message = "OK" });
         }
        
         // /api/users/list
         [ApiActionFilter("用户列表")]
         public IActionResult List(int page, int size)
         {
          page = page < 1 ? 1 : page;
          size = size < 1 ? 1 : size;
          var total = _Users.Count();
          var pages = total % size == 0 ? total / size : ((long)Math.Floor((double)total / size + 1));
          if (page > pages)
          {
          return Ok(new Models.ApiResponse { data = new List<Models.User>(), status = 1, message = "OK", total = total });
          }
          var li = new List<Models.User>();
          var startIndex = page * size - size;
          var endIndex = startIndex + size - 1;
          if (endIndex > total - 1)
          {
          endIndex = total - 1;
          }
          for(; startIndex <= endIndex; startIndex++)
          {
          li.Add(_Users[startIndex]);
          }
          return Ok(new Models.ApiResponse { data = li, status = 1, message = "OK", total = total });
         }
        
         // /api/users/add
         [ApiActionFilter("用户录入")]
         public IActionResult Add()
         {
          return Ok(new Models.ApiResponse { status = 1, message = "OK" });
         }
        
         // /api/users/update
         [ApiActionFilter(new string[] { "用户修改", "用户录入", "用户删除" },ApiActionFilterAttributeOption.AND)]
         public IActionResult Update()
         {
          return Ok(new Models.ApiResponse { status = 1, message = "OK" });
         }
        
         // /api/users/delete
         [ApiActionFilter("用户删除")]
         public IActionResult Delete()
         {
          return Ok(new Models.ApiResponse { status = 1, message = "OK" });
         }
         }
        }
        
        UsersController.cs

        ApiCustomException.cs

        MiddleWares目录下的ApiCustomException.cs文件,是一个数据接口的统一异常处理中间件。

        该文件整理并抄袭自:https://www.cnblogs.com/ShenNan/p/10197231.html

        在此特别?#34892;?#19968;下作者的先行贡献,并请原谅我无耻的抄袭。

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore.Http;
        using Microsoft.AspNetCore.Builder;
        using Microsoft.Extensions.DependencyInjection;
        
        namespace Jwt.Gateway.MiddleWares
        {
         //参考: https://www.cnblogs.com/ShenNan/p/10197231.html
        
         public enum ApiCustomExceptionHandleType
         {
         JsonHandle = 0,
         PageHandle = 1,
         Both = 2
         }
         public class ApiCustomExceptionMiddleWareOption
         {
         public ApiCustomExceptionMiddleWareOption(
          ApiCustomExceptionHandleType handleType = ApiCustomExceptionHandleType.JsonHandle,
          IList<PathString> jsonHandleUrlKeys = null,
          string errorHandingPath = "")
         {
          HandleType = handleType;
          JsonHandleUrlKeys = jsonHandleUrlKeys;
          ErrorHandingPath = errorHandingPath;
         }
         public ApiCustomExceptionHandleType HandleType { get; set; }
         public IList<PathString> JsonHandleUrlKeys { get; set; }
         public PathString ErrorHandingPath { get; set; }
         }
         public class ApiCustomExceptionMiddleWare
         {
         private RequestDelegate _next;
         private ApiCustomExceptionMiddleWareOption _option;
         private IDictionary<int, string> _exceptionStatusCodeDic;
        
         public ApiCustomExceptionMiddleWare(RequestDelegate next, ApiCustomExceptionMiddleWareOption option)
         {
          _next = next;
          _option = option;
          _exceptionStatusCodeDic = new Dictionary<int, string>
          {
          { 401, "?#35789;?#26435;的请求" },
          { 404, "找不到该页面" },
          { 403, "访?#26102;?#25298;绝" },
          { 500, "服务器发生意外的错误" }
          //其余状态自行扩展
          };
         }
        
         public async Task Invoke(HttpContext context)
         {
          Exception exception = null;
          try
          {
          await _next(context);
          }
          catch (Exception ex)
          {
          context.Response.Clear();
          context.Response.StatusCode = 200;//手动设置状态码(总是成功)
          exception = ex;
          }
          finally
          {
          if (_exceptionStatusCodeDic.ContainsKey(context.Response.StatusCode) &&
           !context.Items.ContainsKey("ExceptionHandled"))
          {
           var errorMsg = string.Empty;
           if (context.Response.StatusCode == 500 && exception != null)
           {
           errorMsg = $"{_exceptionStatusCodeDic[context.Response.StatusCode]}\r\n{(exception.InnerException != null ? exception.InnerException.Message : exception.Message)}";
           }
           else
           {
           errorMsg = _exceptionStatusCodeDic[context.Response.StatusCode];
           }
           exception = new Exception(errorMsg);
          }
          if (exception != null)
          {
           var handleType = _option.HandleType;
           if (handleType == ApiCustomExceptionHandleType.Both)
           {
           var requestPath = context.Request.Path;
           handleType = _option.JsonHandleUrlKeys != null && _option.JsonHandleUrlKeys.Count(
            k => requestPath.StartsWithSegments(k, StringComparison.CurrentCultureIgnoreCase)) > 0 ?
            ApiCustomExceptionHandleType.JsonHandle :
            ApiCustomExceptionHandleType.PageHandle;
           }
        
           if (handleType == ApiCustomExceptionHandleType.JsonHandle)
           await JsonHandle(context, exception);
           else
           await PageHandle(context, exception, _option.ErrorHandingPath);
          }
          }
         }
         private Jwt.Gateway.Models.ApiResponse GetApiResponse(Exception ex)
         {
          return new Jwt.Gateway.Models.ApiResponse() { status = 0, message = ex.Message };
         }
         private async Task JsonHandle(HttpContext context, Exception ex)
         {
          var apiResponse = GetApiResponse(ex);
          var serialzeStr = Newtonsoft.Json.JsonConvert.SerializeObject(apiResponse);
          context.Response.ContentType = "application/json";
          await context.Response.WriteAsync(serialzeStr, System.Text.Encoding.UTF8);
         }
         private async Task PageHandle(HttpContext context, Exception ex, PathString path)
         {
          context.Items.Add("Exception", ex);
          var originPath = context.Request.Path;
          context.Request.Path = path;
          try
          {
          await _next(context);
          }
          catch { }
          finally
          {
          context.Request.Path = originPath;
          }
         }
         }
         public static class ApiCustomExceptionMiddleWareExtensions
         {
         public static IApplicationBuilder UseApiCustomException(this IApplicationBuilder app, ApiCustomExceptionMiddleWareOption option)
         {
          return app.UseMiddleware<ApiCustomExceptionMiddleWare>(option);
         }
         }
        }
        
        ApiCustomException.cs

        配置相关

        appsettings.json

        算法'HS256'要求SecurityKey.KeySize大于'128'位,所以JwtSecurityKey可不要太短了哦。

        {
         "Urls": "http://localhost:60000",
         "AllowedHosts": "*",
         "JwtSecurityKey": "areyouokhhhhhhhhhhhhhhhhhhhhhhhhhhh",
         "JwtTokenIssuer": "Jwt.Gateway",
         "JwtTokenAudience": "App"
        }
        
        appsettings.json

        Startup.cs

        关于JWT的配置可以在通过JwtBearerOptions加入一些自己的事件处理逻辑,共有4个事件可供调用:

        OnAuthenticationFailed,OnMessageReceived,OnTokenValidated,OnChallenge, 本示例中是在OnTokenValidated中插入Token黑名单的校验逻辑。黑名单应该是Jwt应用场景中主动使Token过期的主流做法了。

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore.Builder;
        using Microsoft.AspNetCore.Hosting;
        using Microsoft.AspNetCore.Http;
        using Jwt.Gateway.MiddleWares;
        using Microsoft.Extensions.DependencyInjection;
        
        namespace Jwt.Gateway
        {
         public class Startup
         {
          private readonly Microsoft.Extensions.Configuration.IConfiguration _configuration;
        
          public Startup(Microsoft.Extensions.Configuration.IConfiguration configuration)
          {
           _configuration = configuration;
          }
          
          public void ConfigureServices(IServiceCollection services)
          {
           services.AddAuthentication(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options => {
             options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
             {
              /*OnMessageReceived = context =>
              {
               context.Token = context.Request.Query["access_token"];
               return Task.CompletedTask;
              },*/
              OnTokenValidated = context =>
              {
               var token = ((System.IdentityModel.Tokens.Jwt.JwtSecurityToken)context.SecurityToken).RawData;
               if (InBlacklist(token))
               {
                context.Fail("token in blacklist");
               }
               return Task.CompletedTask;
              }
             };
             options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
             {
              ValidateIssuer = true,
              ValidateAudience = true,
              ValidateLifetime = true,
              ValidateIssuerSigningKey = true,
              ValidAudience = _configuration["JwtTokenAudience"],
              ValidIssuer = _configuration["JwtTokenIssuer"],
              IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_configuration["JwtSecurityKey"]))
             };
            });
           services.AddMvc().AddJsonOptions(option=> {
            option.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss.fff";
           });
          }
        
          public void Configure(IApplicationBuilder app, IHostingEnvironment env)
          {
           if (env.IsDevelopment())
           {
            app.UseDeveloperExceptionPage();
           }
        
           app.UseApiCustomException(new ApiCustomExceptionMiddleWareOption(
              handleType: ApiCustomExceptionHandleType.Both,
              jsonHandleUrlKeys: new PathString[] { "/api" },
              errorHandingPath: "/home/error"));
        
           app.UseAuthentication();
        
           app.UseMvc();
          }
        
        
        
          bool InBlacklist(string token)
          {
           //code: 实际项目中应该查询数据库或配置文件进行比对
        
           return false;
          }
        
        
         }
        }
        
        Startup.cs

        Program.cs

        using System;
        using System.Collections.Generic;
        using System.IO;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore;
        using Microsoft.AspNetCore.Hosting;
        using Microsoft.Extensions.Configuration;
        using Microsoft.Extensions.Logging;
        
        namespace Jwt.Gateway
        {
         public class Program
         {
          public static void Main(string[] args)
          {
           BuildWebHost(args).Run();
          }
        
          public static IWebHost BuildWebHost(string[] args)
          {
           var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: true)
            .Build();
        
           return WebHost.CreateDefaultBuilder(args)
            .UseKestrel()
            .UseConfiguration(config)
            .UseStartup<Startup>()
            .Build();
          }
         }
        }
        
        Program.cs

        运行截图

        [运行截图-获取Token]

        [运行截图-配置Fiddler调用接口获取数据]

        [运行截图-获取到数据]

        如果Token校验失败将会返回401错误!

        总结

        以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

        相关文章

        • NetCore WebSocket?#35789;?#36890;讯示例

          NetCore WebSocket?#35789;?#36890;讯示例

          这篇文章主要为大家详细介绍了NetCore WebSocket?#35789;?#36890;讯示例,具有一定的参?#25216;?#20540;,?#34892;?#36259;的小伙伴们可以参?#23478;?#19979;
          2017-06-06
        • 在asp.net网页中显示数学符号的代码

          在asp.net网页中显示数学符号的代码

          在网页中显示一些符号,如数学符号(Insus.NET仅提供常用符号),需要的朋友可以参考下
          2012-10-10
        • .NET发起web请求时维持Session

          .NET发起web请求时维持Session

          一般使用.NET C#发起一个web请求是用WebClient类,应为使用很简单,但是每调用一次OpenRead就会在服务器启用一个新Session,使用HttpWebRequest + CookieContainer就可以让多个web请求只有一个session。
          2009-05-05
        • asp.net button 绑定多个参数

          asp.net button 绑定多个参数

          asp.net button 绑定多个参数的代码
          2008-11-11
        • asp.net coolite 删除时弹出确定按钮

          asp.net coolite 删除时弹出确定按钮

          如果用coolite的 Confirm() 是不知道你选择了什么的 如上代码才可以的
          2009-09-09
        • MVC4制作网站教程第一篇 绪论

          MVC4制作网站教程第一篇 绪论

          MVC4制作网站教程第一篇,创建项目,网站配置,从最基本的操作入手,帮助大家了解MVC4网站入门制作的过程,?#34892;?#36259;的小伙伴们可以参?#23478;?#19979;
          2016-08-08
        • asp.net表单提交时防重复提交并执行前台的JS验证

          asp.net表单提交时防重复提交并执行前台的JS验证

          今天遇到这样的一个情况,就是用户重复提交。?#27604;?#36825;个不能怪用户,只能怪.NET或者服务器反应迟钝,下面有个不错的教程,大家可以参考下
          2013-10-10
        • asp.net(c#)判?#26174;?#31243;图片是否存在

          asp.net(c#)判?#26174;?#31243;图片是否存在

          不错的应用,大家可以拓展到,判?#26174;?#31243;文件是否存在等功能
          2008-09-09
        • asp.NET 脏字过滤算法 修改版

          asp.NET 脏字过滤算法 修改版

          我们网站的脏字?#20540;?#20013;大概有600多个词,而?#19968;?#21457;生变化,因此简单的在数据新增/修改的时候做一次脏字过滤是不够的。在网站从.NET 1.1到2.0改版的时候,对新版的测试发现旧的脏字过滤算法耗费的时间过长,需要做一些优化。
          2009-10-10
        • .net后台页面统一验证是否登录

          .net后台页面统一验证是否登录

          这篇文章主要为大家详细介绍了.net后台页面统一验证是否登录的方法,具有一定的参?#25216;?#20540;,?#34892;?#36259;的小伙伴们可以参?#23478;?#19979;
          2017-04-04

        最新评论

        2018白小姐一肖中特马
        <sup id="sjju3"><noscript id="sjju3"></noscript></sup>
        <big id="sjju3"></big>

      2. <blockquote id="sjju3"></blockquote>
        <blockquote id="sjju3"></blockquote>

          <td id="sjju3"></td>

          <big id="sjju3"></big>
            <code id="sjju3"><strong id="sjju3"><dl id="sjju3"></dl></strong></code>
          1. <sup id="sjju3"><noscript id="sjju3"></noscript></sup>
            <big id="sjju3"></big>

          2. <blockquote id="sjju3"></blockquote>
            <blockquote id="sjju3"></blockquote>

              <td id="sjju3"></td>

              <big id="sjju3"></big>
                <code id="sjju3"><strong id="sjju3"><dl id="sjju3"></dl></strong></code>