diff --git a/README.md b/README.md index 7705a56..42f47bf 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Myvas.AspNetCore.Weixin +# Myvas.AspNetCore.Weixin [![NuGet](https://img.shields.io/nuget/v/Myvas.AspNetCore.Weixin.svg)](https://www.nuget.org/packages/Myvas.AspNetCore.Weixin) [![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/myvas/AspNetCore.Weixin?label=github)](https://github.com/myvas/AspNetCore.Weixin) An ASP.NET Core middleware for Tencent Wechat/Weixin message handling and apis. (微信公众平台/接口调用服务) 微信公众平台/接口调用服务:在微信公众平台上申请服务号或订阅号后,经配置部署可提供自定义菜单、即时信息交流、微信网页授权、模板消息通知等接口调用服务。 -## NuGet -https://www.nuget.org/packages/AspNetCore.Weixin/ +## Demo +http://demo.auth.myvas.com (debian.9-x64) [![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/myvas/AspNetCore.Authentication.Demo?label=github)](https://github.com/myvas/AspNetCore.Authentication.Demo) ## Settings https://mp.weixin.qq.com @@ -159,9 +159,6 @@ $(document).ready(function () { ``` -## Demo -https://wx.myvas.com - ## Dev -* [.NET Core SDK 2.1 LTS](https://dotnet.microsoft.com/download/dotnet-core/2.1) 2.1.802 +* [.NET Core SDK 2.2](https://dotnet.microsoft.com/download/dotnet-core/2.2) * [微信开发者工具](https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html) diff --git a/samples/Demo/Applications/IWeixinEventSink.cs b/samples/Demo/Applications/IWeixinEventSink.cs deleted file mode 100644 index a8104bf..0000000 --- a/samples/Demo/Applications/IWeixinEventSink.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Threading.Tasks; -using AspNetCore.Weixin; - -namespace Demo.Applications -{ - public interface IWeixinEventSink - { - Task OnClickMenuEventReceived(object sender, ClickMenuEventReceivedEventArgs e); - Task OnEnterEventReceived(object sender, EnterEventReceivedEventArgs e); - Task OnImageMessageReceived(object sender, ImageMessageReceivedEventArgs e); - Task OnLinkMessageReceived(object sender, LinkMessageReceivedEventArgs e); - Task OnLocationEventReceived(object sender, LocationEventReceivedEventArgs e); - Task OnLocationMessageReceived(object sender, LocationMessageReceivedEventArgs e); - Task OnQrscanEventReceived(object sender, QrscanEventReceivedEventArgs e); - Task OnShortVideoMessageReceived(object sender, ShortVideoMessageReceivedEventArgs e); - Task OnSubscribeEventReceived(object sender, SubscribeEventReceivedEventArgs e); - Task OnTextMessageReceived(object sender, TextMessageReceivedEventArgs e); - Task OnUnsubscribeEventReceived(object sender, UnsubscribeEventReceivedEventArgs e); - Task OnVideoMessageReceived(object sender, VideoMessageReceivedEventArgs e); - Task OnViewMenuEventReceived(object sender, ViewMenuEventReceivedEventArgs e); - Task OnVoiceMessageReceived(object sender, VoiceMessageReceivedEventArgs e); - } -} \ No newline at end of file diff --git a/samples/Demo/Applications/WeixinEventSink.cs b/samples/Demo/Applications/WeixinEventSink.cs deleted file mode 100644 index a725342..0000000 --- a/samples/Demo/Applications/WeixinEventSink.cs +++ /dev/null @@ -1,312 +0,0 @@ -using AspNetCore.Weixin; -using Demo.Data; -using Demo.Entities; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Demo.Applications -{ - public class WeixinEventSink : IWeixinEventSink - { - private readonly ILogger _logger; - private readonly WeixinDbContext _db; - - public WeixinEventSink(ILoggerFactory loggerFactory, - WeixinDbContext db) - { - _logger = loggerFactory?.CreateLogger() ?? throw new ArgumentNullException(nameof(loggerFactory)); - _db = db ?? throw new ArgumentNullException(nameof(db)); - } - - public async Task OnTextMessageReceived(object sender, TextMessageReceivedEventArgs e) - { - _logger.LogDebug(XmlConvert.SerializeObject(e)); - - var msg = new ReceivedTextMessage(); - msg.Content = e.Content; - msg.From = e.FromUserName; - msg.To = e.ToUserName; - msg.ReceivedTime = new DateTimeOffset(WeixinTimestampHelper.ToUtcTime(e.CreateTimeStr)); - _db.ReceivedTextMessages.Add(msg); - var saveResult = await _db.SaveChangesAsync(); - if (saveResult > 0) - { - _logger.LogDebug($"已将微信文本消息存入数据库。Result:{saveResult}, From:{msg.From}, To:{msg.To}, Time:{msg.ReceivedTime}, Content:{msg.Content}"); - } - _logger.LogDebug($"微信文本消息在数据库中共{_db.ReceivedTextMessages.ToList().Count()}条记录。"); - - - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageText(); - { - var result = new StringBuilder(); - result.AppendFormat("您刚才发送了文本信息:{0}", e.Content); - - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Content = result.ToString(); - } - await messageHandler.WriteAsync(responseMessage); - - _logger.LogDebug(XmlConvert.SerializeObject(responseMessage)); - - return true; - } - - public async Task OnLinkMessageReceived(object sender, LinkMessageReceivedEventArgs e) - { - _logger.LogInformation($"OnLinkMessageReceived: {e.Url}"); - - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageText(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Content = string.Format(@"您发送了一条链接信息: - Title:{0} - Description:{1} - Url:{2}", e.Title, e.Description, e.Url); - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnVideoMessageReceived(object sender, VideoMessageReceivedEventArgs e) - { - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageText(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Content = "您发送了一条视频信息,ID:" + e.MediaId; - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnShortVideoMessageReceived(object sender, ShortVideoMessageReceivedEventArgs e) - { - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageText(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Content = "您发送了一条小视频信息,ID:" + e.MediaId; - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnVoiceMessageReceived(object sender, VoiceMessageReceivedEventArgs e) - { - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageVoice(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Voice.MediaId = e.MediaId; - //responseMessage.Music.MusicUrl = e.MediaId; - //responseMessage.Music.Title = "语音"; - //responseMessage.Music.Description = "这里是一条语音消息"; - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnImageMessageReceived(object sender, ImageMessageReceivedEventArgs e) - { - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageNews(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Articles.Add(new Article() - { - Title = "您刚才发送了图片信息", - Description = "您发送的图片将会显示在边上", - PicUrl = e.PicUrl, - Url = "http://wx.demo.com" - }); - responseMessage.Articles.Add(new Article() - { - Title = "第二条", - Description = "第二条带链接的内容", - PicUrl = e.PicUrl, - Url = "http://wx.demo.com" - }); - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnLocationMessageReceived(object sender, LocationMessageReceivedEventArgs e) - { - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageNews(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - - var markersList = new List(); - markersList.Add(new GoogleMapMarkers() - { - Latitude = e.Latitude, - Longitude = e.Longitude, - Color = "red", - Label = "S", - Size = GoogleMapMarkerSize.Default, - }); - var mapSize = "480x600"; - var mapUrl = GoogleMapHelper.GetGoogleStaticMap(19 /*requestMessage.Scale*//*微信和GoogleMap的Scale不一致,这里建议使用固定值*/, - markersList, mapSize); - responseMessage.Articles.Add(new Article() - { - Description = string.Format("您刚才发送了地理位置信息。Location_X:{0},Location_Y:{1},Scale:{2},标签:{3}", - e.Latitude, e.Longitude, - e.Scale, e.Label), - PicUrl = mapUrl, - Title = "定位地点周边地图", - Url = mapUrl - }); - responseMessage.Articles.Add(new Article() - { - Title = "AspNetCore.Weixin", - Description = "AspNetCore.Weixin", - PicUrl = "http://wx.demo.com/logo.jpg", - Url = "http://wx.demo.com" - }); - - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnLocationEventReceived(object sender, LocationEventReceivedEventArgs e) - { - //这里是微信客户端(通过微信服务器)自动发送过来的位置信息 - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageText(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Content = string.Format("刚刚上报了一条定位信息:(Lat={0},Lon={1},Prc={2})", - e.Latitude, e.Longitude, e.Precision); - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnClickMenuEventReceived(object sender, ClickMenuEventReceivedEventArgs e) - { - _logger.LogDebug("点击了子菜单按钮({0}): {1}", e.FromUserName, e.MenuItemKey); - - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageText(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Content = string.Format("点击了子菜单按钮({0}): {1}", e.FromUserName, e.MenuItemKey); - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnViewMenuEventReceived(object sender, ViewMenuEventReceivedEventArgs e) - { - _logger.LogDebug("点击了子菜单按钮({0}): {1}", e.FromUserName, e.Url); - - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageText(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Content = string.Format("点击了子菜单按钮({0}): {1}", e.FromUserName, e.Url); - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnUnsubscribeEventReceived(object sender, UnsubscribeEventReceivedEventArgs e) - { - _logger.LogDebug("Unsubscribe({0})", e.FromUserName); - - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageText(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Content = string.Format("Unsubscribe({0})", e.FromUserName); - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnEnterEventReceived(object sender, EnterEventReceivedEventArgs e) - { - var messageHandler = sender as WeixinMessageHandler; - _logger.LogDebug("Subscribe: from:{0}", e.FromUserName); - - var responseMessage = new ResponseMessageNews(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Articles.Add(new Article() - { - Title = "欢迎进入AspNetCore.Weixin演示系统", - Description = "由AspNetCore.Weixin提供", - PicUrl = "https://mp.weixin.qq.com/cgi-bin/getimgdata?mode=large&source=file&fileId=200121314%3E&token=977619473&lang=zh_CN", - Url = "http://wx.demo.com" - }); - await messageHandler.WriteAsync(responseMessage); - - return true; - } - - public async Task OnSubscribeEventReceived(object sender, SubscribeEventReceivedEventArgs e) - { - var messageHandler = sender as WeixinMessageHandler; - if (string.IsNullOrWhiteSpace(e.EventKey)) - { - _logger.LogDebug("Subscribe: from:{0}", e.FromUserName); - - var responseMessage = new ResponseMessageNews(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Articles.Add(new Article() - { - Title = "欢迎体验AspNetCore.Weixin演示系统", - Description = "由AspNetCore.Weixin提供", - PicUrl = "https://mp.weixin.qq.com/cgi-bin/getimgdata?mode=large&source=file&fileId=200121314%3E&token=977619473&lang=zh_CN", - Url = "http://wx.demo.com" - }); - await messageHandler.WriteAsync(responseMessage); - } - else - { - _logger.LogDebug("Subscribe w/ scene({0}): {1}, {2}", e.FromUserName, e.EventKey, e.Ticket); - - - var responseMessage = new ResponseMessageNews(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Articles.Add(new Article() - { - Title = "欢迎体验AspNetCore.Weixin演示系统", - Description = "由AspNetCore.Weixin提供。此消息带场景({e.EventKey}, {e.Ticket})", - PicUrl = "https://mp.weixin.qq.com/cgi-bin/getimgdata?mode=large&source=file&fileId=200121314%3E&token=977619473&lang=zh_CN", - Url = "http://wx.demo.com" - }); - await messageHandler.WriteAsync(responseMessage); - } - - return true; - } - - public async Task OnQrscanEventReceived(object sender, QrscanEventReceivedEventArgs e) - { - _logger.LogDebug("Qrscan({0}): {1}, {2}", e.FromUserName, e.EventKey, e.Ticket); - - var messageHandler = sender as WeixinMessageHandler; - var responseMessage = new ResponseMessageText(); - responseMessage.FromUserName = e.ToUserName; - responseMessage.ToUserName = e.FromUserName; - responseMessage.Content = string.Format("Qrscan({0}): {1}, {2}", e.FromUserName, e.EventKey, e.Ticket); - await messageHandler.WriteAsync(responseMessage); - - return true; - } - } -} diff --git a/samples/Demo/Controllers/HomeController.cs b/samples/Demo/Controllers/HomeController.cs deleted file mode 100644 index c92a7f3..0000000 --- a/samples/Demo/Controllers/HomeController.cs +++ /dev/null @@ -1,103 +0,0 @@ -using AspNetCore.Weixin; -using Demo.Data; -using Demo.Entities; -using Demo.Models.Home; -using Demo.Models; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Controllers -{ - public class HomeController : Controller - { - private readonly WeixinDbContext _db; - private readonly ILogger _logger; - private readonly IWeixinAccessToken _weixinAccessToken; - - public HomeController( - WeixinDbContext db, - ILoggerFactory loggerFactory, - IWeixinAccessToken smsSender) - { - _db = db ?? throw new ArgumentNullException(nameof(db)); - _logger = loggerFactory?.CreateLogger() ?? throw new ArgumentNullException(nameof(loggerFactory)); - _weixinAccessToken = smsSender ?? throw new ArgumentNullException(nameof(smsSender)); - } - - public IActionResult Index() - { - var subscriberCount = _db.Subscribers.Count(); - var receivedTextCount = _db.ReceivedTextMessages.Count(); - var vm = new IndexViewModel() { SubscriberCount = subscriberCount, ReceivedTextCount = receivedTextCount }; - - return View(vm); - } - - public async Task Subscribers() - { - var vm = new ReturnableViewModel>(); - - var token = _weixinAccessToken.GetToken(); - var subscribers = await UserApi.GetAllUserInfo(token); - vm.Item = subscribers; - - return View(vm); - } - - - public async Task ReceivedText() - { - var items = await _db.ReceivedTextMessages.ToListAsync(); - _logger.LogDebug($"΢ıϢݿй{_db.ReceivedTextMessages.ToList().Count()}¼"); - return View(items); - } - - public async Task SendWeixin(string openId) - { - if (string.IsNullOrEmpty(openId)) - { - return View(); - } - - var vm = new SendWeixinViewModel(); - vm.Received = await _db.ReceivedTextMessages.Where(x => x.To == openId).ToListAsync(); - vm.OpenId = openId; - return View(vm); - } - - [HttpPost, ActionName(nameof(SendWeixin))] - [ValidateAntiForgeryToken] - public async Task SendWeixin_Post(SendWeixinViewModel vm) - { - if (!ModelState.IsValid) - { - return View(vm); - } - - var token = _weixinAccessToken.GetToken(); - var result = await Custom.SendText(token, vm.OpenId, vm.Content); - if (!result.Succeeded) - { - ModelState.AddModelError("", result.errmsg); - return View(vm); - } - - return RedirectToAction(nameof(Index)); - } - - public IActionResult About() - { - return View(); - } - - public IActionResult Error() - { - return View(); - } - } -} \ No newline at end of file diff --git a/samples/Demo/Controllers/JssdkController.cs b/samples/Demo/Controllers/JssdkController.cs deleted file mode 100644 index 8605cd0..0000000 --- a/samples/Demo/Controllers/JssdkController.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Demo.Entities; -using Demo.Models; -using AspNetCore.Weixin; -using Microsoft.Extensions.Options; - -namespace Demo.Controllers -{ - public class JssdkController : Controller - { - private readonly IWeixinAccessToken _weixinAccessToken; - private readonly IWeixinJsapiTicket _weixinJsapiTicket; - private readonly WeixinAccessTokenOptions _options; - - public JssdkController( - IWeixinAccessToken weixinAccessToken, - IWeixinJsapiTicket weixinJsapiTicket, - IOptions optionsAccessor) - { - _weixinAccessToken = weixinAccessToken ?? throw new ArgumentNullException(nameof(weixinAccessToken)); - _weixinJsapiTicket = weixinJsapiTicket ?? throw new ArgumentNullException(nameof(weixinJsapiTicket)); - _options = optionsAccessor?.Value ?? throw new ArgumentNullException(nameof(optionsAccessor)); - } - - public IActionResult Index() - { - var vm = new ShareJweixinViewModel(); - - var config = new WeixinJsConfig() - { - debug = true, - appId = _options.AppId - }; - var jsapiTicket = _weixinJsapiTicket.GetTicket(); - var refererUrl = Request.GetAbsoluteUri();// Url.AbsoluteContent(Url.Action()); - vm.ConfigJson = config.ToJson(jsapiTicket, refererUrl); - - vm.Title = "链接分享测试"; - vm.Url = "http://wx.steamlet.com/about"; - vm.Description = "链接分享测试"; - vm.ImgUrl = "http://wx.steamlet.com/img/mp-test.jpg"; - return View(vm); - } - } -} \ No newline at end of file diff --git a/samples/Demo/Controllers/QrcodeController.cs b/samples/Demo/Controllers/QrcodeController.cs deleted file mode 100644 index 418d337..0000000 --- a/samples/Demo/Controllers/QrcodeController.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using AspNetCore.Weixin; -using Microsoft.Extensions.Logging; -using System.IO; -using System.Net; - -namespace Demo.Controllers -{ - public class QrcodeController : Controller - { - private readonly ILogger _logger; - private readonly IWeixinAccessToken _weixinAccessToken; - - public QrcodeController(ILoggerFactory loggerFactory, - IWeixinAccessToken weixinAccessToken) - { - _logger = loggerFactory?.CreateLogger() ?? throw new ArgumentNullException(nameof(loggerFactory)); - _weixinAccessToken = weixinAccessToken ?? throw new ArgumentNullException(nameof(weixinAccessToken)); - } - - public IActionResult Index() - { - return View(); - } - - [HttpGet("[controller]/[action]/{scene}")] - public async Task UrlWithScene(string scene) - { - var accessToken = _weixinAccessToken.GetToken(); - var createQrcodeResult = await QrCode.Create(accessToken, "QR_LIMIT_STR_SCENE", scene); - return Json(createQrcodeResult); - } - - [HttpGet("[controller]/[action]/{scene}")] - public async Task QrcodeWithScene(string scene) - { - var accessToken = _weixinAccessToken.GetToken(); - var createQrcodeResult = await QrCode.Create(accessToken, "QR_LIMIT_STR_SCENE", scene); - - var url = QrCode.ShowQrcode(createQrcodeResult.ticket); - - return Content(url); - } - } -} \ No newline at end of file diff --git a/samples/Demo/Controllers/WeixinAccessTokenController.cs b/samples/Demo/Controllers/WeixinAccessTokenController.cs deleted file mode 100644 index 169139f..0000000 --- a/samples/Demo/Controllers/WeixinAccessTokenController.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AspNetCore.Weixin; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace Demo.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class WeixinAccessTokenController : ControllerBase - { - private readonly IWeixinAccessToken _weixinAccessToken; - - public WeixinAccessTokenController(IWeixinAccessToken weixinAccessToken) - { - _weixinAccessToken = weixinAccessToken; - } - - public string GetToken() - { - return _weixinAccessToken.GetToken(); - } - } -} \ No newline at end of file diff --git a/samples/Demo/Controllers/WeixinMenuController.cs b/samples/Demo/Controllers/WeixinMenuController.cs deleted file mode 100644 index d421ad3..0000000 --- a/samples/Demo/Controllers/WeixinMenuController.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AspNetCore.Weixin; -using Demo.Data; -using Demo.Entities; -using Demo.Models; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; - -namespace Demo.Controllers -{ - [Authorize]//(Policy = "WeixinMenuManager")] - public class WeixinMenuController : Controller - { - private readonly IdentityDbContext _context; - private readonly IWeixinAccessToken _weixinAccessToken; - private readonly ILogger _logger; - - public WeixinMenuController(IdentityDbContext context, - IWeixinAccessToken weixinAccessToken, - ILogger logger) - { - _context = context; - _weixinAccessToken = weixinAccessToken; - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - public async Task Index() - { - var token = _weixinAccessToken.GetToken(); - var resultJson = await MenuApi.GetMenuAsync(token); - - var vm = new WeixinJsonViewModel - { - Token = token, - Json = JsonConvert.SerializeObject(resultJson, Formatting.Indented) - }; - return View(vm); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task UpdateWeixinMenu(WeixinJsonViewModel vm) - { - if (ModelState.IsValid) - { - if (!string.IsNullOrEmpty(vm.Json)) - { - var token = _weixinAccessToken.GetToken(); - var result = await MenuApi.CreateMenuAsync(token, vm.Json); - - _logger.LogDebug(result.ToString()); - - return View("UpdateMenuResult", result); - } - } - - // If we got this far, something failed; redisplay form. - return RedirectToAction(nameof(Index), new { input = vm.Json }); - } - } -} \ No newline at end of file diff --git a/samples/Demo/Core/Entities/Entity.cs b/samples/Demo/Core/Entities/Entity.cs deleted file mode 100644 index d0aa9ed..0000000 --- a/samples/Demo/Core/Entities/Entity.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace System -{ - - /// - /// A shortcut of for most used primary key type (). - /// - public abstract class Entity : Entity - { - public Entity() - { - Id = ShortGuid.NewGuid().ToString(); - } - } - - /// - /// Basic implementation of IEntity interface. - /// An entity can inherit this class of directly implement to IEntity interface. - /// - /// Type of the primary key of the entity - public abstract class Entity : IEntity - where TKey : IEquatable - { - /// - /// Unique identifier for this entity. - /// - public virtual TKey Id { get; set; } - - /// - /// Checks if this entity is transient (it has not an Id). - /// - /// True, if this entity is transient - public virtual bool IsTransient() - { - if (EqualityComparer.Default.Equals(Id, default(TKey))) - { - return true; - } - - //Workaround for EF Core since it sets int/long to min value when attaching to dbcontext - if (typeof(TKey) == typeof(int)) - { - return Convert.ToInt32(Id) <= 0; - } - - if (typeof(TKey) == typeof(long)) - { - return Convert.ToInt64(Id) <= 0; - } - - return false; - } - - /// - public override bool Equals(object obj) - { - if (obj == null || !(obj is Entity)) - { - return false; - } - - //Same instances must be considered as equal - if (ReferenceEquals(this, obj)) - { - return true; - } - - //Transient objects are not considered as equal - var other = (Entity)obj; - if (IsTransient() && other.IsTransient()) - { - return false; - } - - //Must have a IS-A relation of types or must be same type - var typeOfThis = GetType(); - var typeOfOther = other.GetType(); - if (!typeOfThis.IsAssignableFrom(typeOfOther) && !typeOfOther.IsAssignableFrom(typeOfThis)) - { - return false; - } - - return Id.Equals(other.Id); - } - - /// - public override int GetHashCode() - { - return Id.GetHashCode(); - } - - /// - public static bool operator ==(Entity left, Entity right) - { - if (Equals(left, null)) - { - return Equals(right, null); - } - - return left.Equals(right); - } - - /// - public static bool operator !=(Entity left, Entity right) - { - return !(left == right); - } - - /// - public override string ToString() - { - return $"[{GetType().Name} {Id}]"; - } - } -} diff --git a/samples/Demo/Core/Entities/IEntity.cs b/samples/Demo/Core/Entities/IEntity.cs deleted file mode 100644 index e494475..0000000 --- a/samples/Demo/Core/Entities/IEntity.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace System -{ - /// - /// A shortcut of for most used primary key type (). - /// - public interface IEntity : IEntity - { - - } - - /// - /// Defines interface for base entity type. All entities in the system must implement this interface. - /// - /// Type of the primary key of the entity - public interface IEntity - where TKey : IEquatable - { - /// - /// Unique identifier for this entity. - /// - TKey Id { get; set; } - - /// - /// Checks if this entity is transient (not persisted to database and it has not an ). - /// - /// True, if this entity is transient - //bool IsTransient(); - } -} diff --git a/samples/Demo/Core/Entities/ShortGuid.cs b/samples/Demo/Core/Entities/ShortGuid.cs deleted file mode 100644 index 81eac8b..0000000 --- a/samples/Demo/Core/Entities/ShortGuid.cs +++ /dev/null @@ -1,283 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace System -{ - /// - /// A base64-like shorter and url friendly Guid class. - /// - /// It takes a standard GUID like this: - /// c9a646d3-9c61-4cb7-bfcd-ee2522c8f633 - /// And converts it into this smaller string: - /// 00amyWGct0y_ze4lIsj2Mw - /// - /// - /// - /// ref http://www.singular.co.nz/2007/12/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp/ - /// - - /// - /// Represents a globally unique identifier (GUID) with a - /// shorter string value. Sguid - /// - public struct ShortGuid - { - #region Static - - /// - /// A read-only instance of the ShortGuid class whose value - /// is guaranteed to be all zeroes. - /// - public static readonly ShortGuid Empty = new ShortGuid(Guid.Empty); - - #endregion - - #region Fields - - Guid _guid; - string _value; - - #endregion - - #region Contructors - - /// - /// Creates a ShortGuid from a base64 encoded string - /// - /// The encoded guid as a - /// base64 string - public ShortGuid(string value) - { - _value = value; - _guid = Decode(value); - } - - /// - /// Creates a ShortGuid from a Guid - /// - /// The Guid to encode - public ShortGuid(Guid guid) - { - _value = Encode(guid); - _guid = guid; - } - - #endregion - - #region Properties - - /// - /// Gets/sets the underlying Guid - /// - public Guid Guid - { - get { return _guid; } - set - { - if (value != _guid) - { - _guid = value; - _value = Encode(value); - } - } - } - - /// - /// Gets/sets the underlying base64 encoded string - /// - public string Value - { - get { return _value; } - set - { - if (value != _value) - { - _value = value; - _guid = Decode(value); - } - } - } - - #endregion - - #region ToString - - /// - /// Returns the base64 encoded guid as a string - /// - /// - public override string ToString() - { - return _value; - } - - #endregion - - #region Equals - - /// - /// Returns a value indicating whether this instance and a - /// specified Object represent the same type and value. - /// - /// The object to compare - /// - public override bool Equals(object obj) - { - if (obj is ShortGuid) - return _guid.Equals(((ShortGuid)obj)._guid); - if (obj is Guid) - return _guid.Equals((Guid)obj); - if (obj is string) - return _guid.Equals(((ShortGuid)obj)._guid); - return false; - } - - #endregion - - #region GetHashCode - - /// - /// Returns the HashCode for underlying Guid. - /// - /// - public override int GetHashCode() - { - return _guid.GetHashCode(); - } - - #endregion - - #region NewGuid - - /// - /// Initialises a new instance of the ShortGuid class - /// - /// - public static ShortGuid NewGuid() - { - return new ShortGuid(Guid.NewGuid()); - } - - #endregion - - #region Encode - - /// - /// Creates a new instance of a Guid using the string value, - /// then returns the base64 encoded version of the Guid. - /// - /// An actual Guid string (i.e. not a ShortGuid) - /// - public static string Encode(string value) - { - Guid guid = new Guid(value); - return Encode(guid); - } - - /// - /// Encodes the given Guid as a base64 string that is 22 - /// characters long. - /// - /// The Guid to encode - /// - public static string Encode(Guid guid) - { - string encoded = Convert.ToBase64String(guid.ToByteArray()); - encoded = encoded - .Replace("/", "_") - .Replace("+", "-"); - return encoded.Substring(0, 22); - } - - #endregion - - #region Decode - - /// - /// Decodes the given base64 string - /// - /// The base64 encoded string of a Guid - /// A new Guid - public static Guid Decode(string value) - { - value = value - .Replace("_", "/") - .Replace("-", "+"); - byte[] buffer = Convert.FromBase64String(value + "=="); - return new Guid(buffer); - } - - #endregion - - #region Operators - - /// - /// Determines if both ShortGuids have the same underlying - /// Guid value. - /// - /// - /// - /// - public static bool operator ==(ShortGuid x, ShortGuid y) - { - if ((object)x == null) return (object)y == null; - return x._guid == y._guid; - } - - /// - /// Determines if both ShortGuids do not have the - /// same underlying Guid value. - /// - /// - /// - /// - public static bool operator !=(ShortGuid x, ShortGuid y) - { - return !(x == y); - } - - /// - /// Implicitly converts the ShortGuid to it's string equivilent - /// - /// - /// - public static implicit operator string(ShortGuid shortGuid) - { - return shortGuid._value; - } - - /// - /// Implicitly converts the ShortGuid to it's Guid equivilent - /// - /// - /// - public static implicit operator Guid(ShortGuid shortGuid) - { - return shortGuid._guid; - } - - /// - /// Implicitly converts the string to a ShortGuid - /// - /// - /// - public static implicit operator ShortGuid(string shortGuid) - { - return new ShortGuid(shortGuid); - } - - /// - /// Implicitly converts the Guid to a ShortGuid - /// - /// - /// - public static implicit operator ShortGuid(Guid guid) - { - return new ShortGuid(guid); - } - - #endregion - } -} diff --git a/samples/Demo/Core/ViewModels/ReturnableViewModel.cs b/samples/Demo/Core/ViewModels/ReturnableViewModel.cs deleted file mode 100644 index ddebd92..0000000 --- a/samples/Demo/Core/ViewModels/ReturnableViewModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Models -{ - public class ReturnableViewModel - { - //[Url] //相对路径不符合要求 - [Display(Name = "返回")] - public string ReturnUrl { get; set; } - } - - /// - /// - /// - /// 不允许接口,必须可以实例化。可以是值类型、基础类型、无参构造自定义类。 - public class ReturnableViewModel : ReturnableViewModel - where T : class - { - public T Item { get; set; } - } -} diff --git a/samples/Demo/Core/ViewModels/SearchViewModel.cs b/samples/Demo/Core/ViewModels/SearchViewModel.cs deleted file mode 100644 index 6a05549..0000000 --- a/samples/Demo/Core/ViewModels/SearchViewModel.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Models -{ - public class SearchViewModel : ReturnableViewModel - { - [Display(Name = "搜索")] - public string SearchString { get; set; } - } - - public class SearchViewModel : SearchViewModel - where TEntity : IEntity - { - } - - - public class SearchViewModel : ReturnableViewModel - where TEntity : IEntity - where TKey : IEquatable - { - [Display(Name = "搜索")] - public string SearchString { get; set; } - - public IList Items { get; set; } - - public SearchViewModel() - { - Items = new List(); - } - } -} diff --git a/samples/Demo/Data/Identity/IdentityDbContext.cs b/samples/Demo/Data/Identity/IdentityDbContext.cs deleted file mode 100644 index 353c647..0000000 --- a/samples/Demo/Data/Identity/IdentityDbContext.cs +++ /dev/null @@ -1,19 +0,0 @@ -using AspNetCore.Weixin; -using Demo.Entities; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Data -{ - public class IdentityDbContext : IdentityDbContext - { - public IdentityDbContext(DbContextOptions options) - : base(options) - { - } - } -} diff --git a/samples/Demo/Data/Identity/IdentityDbInitializer.cs b/samples/Demo/Data/Identity/IdentityDbInitializer.cs deleted file mode 100644 index 15c9ea0..0000000 --- a/samples/Demo/Data/Identity/IdentityDbInitializer.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Demo.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Data -{ - public static class IdentityDbInitializer - { - public const string AdminUserName = "Admin"; - public const string AdminInitPassword = "P@ssw0rd"; - - public static async Task Initialize(IServiceProvider serviceProvider) - { - using (var db = new IdentityDbContext(serviceProvider.GetRequiredService>())) - { - await EnsureAdminUser(serviceProvider); - } - } - - private static async Task EnsureAdminUser(IServiceProvider serviceProvider) - { - var userManager = serviceProvider.GetRequiredService>(); - - var user = await userManager.FindByNameAsync(AdminUserName); - if (user == null) - { - user = new AppUser() - { - UserName = AdminUserName, - Email = "admin@myvas.com", - EmailConfirmed = true, - PhoneNumber = "13800138000", - PhoneNumberConfirmed = true, - }; - var result = await userManager.CreateAsync(user, AdminInitPassword); - if (!result.Succeeded) - { - throw new Exception(GetErrorMessage(result)); - } - } - } - - private static string GetErrorMessage(IdentityResult identityResult) - { - var result = ""; - - foreach (var error in identityResult.Errors) - { - result += $"[{error.Code}]{error.Description}" + Environment.NewLine; - } - return result; - } - - - } -} diff --git a/samples/Demo/Data/WebHostDatabaseExtensions.cs b/samples/Demo/Data/WebHostDatabaseExtensions.cs deleted file mode 100644 index 506902c..0000000 --- a/samples/Demo/Data/WebHostDatabaseExtensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Data -{ - public static class WebHostDatabaseExtensions - { - public static IWebHost MigrateDatabase(this IWebHost webHost) - { - using (var scope = webHost.Services.CreateScope()) - { - var services = scope.ServiceProvider; - - try - { - var db = services.GetRequiredService(); - db.Database.Migrate(); - } - catch (Exception ex) - { - var logger = services.GetRequiredService>(); - logger.LogError(ex, "An error occurred while migrating the Identity db."); - } - - try - { - var db = services.GetRequiredService(); - db.Database.Migrate(); - } - catch (Exception ex) - { - var logger = services.GetRequiredService>(); - logger.LogError(ex, "An error occurred while migrating the Weixin db."); - } - } - - return webHost; - } - - public static IWebHost SeedDatabase(this IWebHost webHost) - { - using (var scope = webHost.Services.CreateScope()) - { - var services = scope.ServiceProvider; - - try - { - IdentityDbInitializer.Initialize(services).Wait(); - } - catch (Exception ex) - { - var logger = services.GetRequiredService>(); - logger.LogError(ex, "An error occurred while seeding the database."); - } - } - - return webHost; - } - } -} diff --git a/samples/Demo/Data/Weixin/WeixinDbContext.cs b/samples/Demo/Data/Weixin/WeixinDbContext.cs deleted file mode 100644 index 5583b5a..0000000 --- a/samples/Demo/Data/Weixin/WeixinDbContext.cs +++ /dev/null @@ -1,22 +0,0 @@ -using AspNetCore.Weixin; -using Demo.Entities; -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Data -{ - public class WeixinDbContext:DbContext - { - public WeixinDbContext(DbContextOptions options) : base(options) - { - } - - public DbSet Subscribers { get; set; } - - public DbSet ReceivedTextMessages { get; set; } - } -} diff --git a/samples/Demo/Demo.csproj b/samples/Demo/Demo.csproj deleted file mode 100644 index df4e393..0000000 --- a/samples/Demo/Demo.csproj +++ /dev/null @@ -1,49 +0,0 @@ - - - - netcoreapp2.2 - wxdemo - wxdemo - Demo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(IncludeRazorContentInPack) - - - PreserveNewest - - - - - - PreserveNewest - - - - diff --git a/samples/Demo/Entities/Identity/AppUser.cs b/samples/Demo/Entities/Identity/AppUser.cs deleted file mode 100644 index 4696452..0000000 --- a/samples/Demo/Entities/Identity/AppUser.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.AspNetCore.Identity; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Entities -{ - public class AppUser : IdentityUser - { - } -} diff --git a/samples/Demo/Entities/Identity/AppUserManager.cs b/samples/Demo/Entities/Identity/AppUserManager.cs deleted file mode 100644 index 5a1ea62..0000000 --- a/samples/Demo/Entities/Identity/AppUserManager.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Entities -{ - public class AppUserManager : UserManager - { - public AppUserManager(IUserStore store, - IOptions optionsAccessor, - IPasswordHasher passwordHasher, - IEnumerable> userValidators, - IEnumerable> passwordValidators, - ILookupNormalizer keyNormalizer, - IdentityErrorDescriber errors, - IServiceProvider services, - ILogger> logger) - : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) - { - } - - public virtual async Task FindByPhoneNumberAsync(string phoneNumber) - { - return await Users.FirstOrDefaultAsync(x => x.PhoneNumber == phoneNumber); - } - - public virtual async Task VerifyAndConfirmPhoneNumberAsync(AppUser user, string code) - { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } - - if (!await VerifyUserTokenAsync(user, Options.Tokens.ChangePhoneNumberTokenProvider, ChangePhoneNumberTokenPurpose + ":" + user.PhoneNumber, code)) - { - return IdentityResult.Failed(ErrorDescriber.InvalidToken()); - } - - user.PhoneNumberConfirmed = true; - return await UpdateAsync(user); - } - - public virtual async Task<(AppUser user, string code)> GenerateChangePhoneNumberTokenAsync(string phoneNumber) - { - var user = await FindByPhoneNumberAsync(phoneNumber); - if (user == null) - { - user = new AppUser { UserName = phoneNumber, PhoneNumber = phoneNumber }; - var identityResult = await CreateAsync(user); - if (!identityResult.Succeeded) - { - var errMsg = ""; - foreach (var err in identityResult.Errors) - { - errMsg += $"[{err.Code}]{err.Description}"; - } - throw new ApplicationException($"Failed on creating a user.{errMsg}"); - } - } - - var code = await GenerateChangePhoneNumberTokenAsync(user, phoneNumber); - return (user, code); - } - - - public virtual async Task<(AppUser user, IdentityResult result)> VerifyChangePhoneNumberTokenAsync(string phoneNumber, string token) - { - if (string.IsNullOrEmpty(phoneNumber)) - { - throw new ArgumentNullException(nameof(phoneNumber)); - } - if (string.IsNullOrEmpty(token)) - { - throw new ArgumentNullException(nameof(token)); - } - var user = await FindByPhoneNumberAsync(phoneNumber); - if (user == null) - { - throw new ArgumentNullException($"User associated with phone number {phoneNumber} not exists!"); - } - - // Make sure the token is valid and the stamp matches - //var result = VerifyUserTokenAsync(user, Options.Tokens.ChangePhoneNumberTokenProvider, ChangePhoneNumberTokenPurpose + ":" + phoneNumber, token).Result; - var result = await VerifyAndConfirmPhoneNumberAsync(user, token); - return (user, result); - } - } -} diff --git a/samples/Demo/Entities/Weixin/ReceivedTextMessage.cs b/samples/Demo/Entities/Weixin/ReceivedTextMessage.cs deleted file mode 100644 index 567a8d1..0000000 --- a/samples/Demo/Entities/Weixin/ReceivedTextMessage.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Entities -{ - public class ReceivedTextMessage : Entity - { - public string From { get; set; } - public string To { get; set; } - public DateTimeOffset ReceivedTime { get; set; } - public string Content { get; set; } - } -} diff --git a/samples/Demo/Entities/Weixin/WeixinSubscriber.cs b/samples/Demo/Entities/Weixin/WeixinSubscriber.cs deleted file mode 100644 index f626f36..0000000 --- a/samples/Demo/Entities/Weixin/WeixinSubscriber.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Entities -{ - public class WeixinSubscriber :Entity - { - public string OpenId { get; set; } - public string Gender { get; set; } - public string NickName { get; set; } - public string AvatorImage { get; set; } - } -} diff --git a/samples/Demo/Extensions/AbsoluteActionUrlHelperExtensions.cs b/samples/Demo/Extensions/AbsoluteActionUrlHelperExtensions.cs deleted file mode 100644 index d4f71f7..0000000 --- a/samples/Demo/Extensions/AbsoluteActionUrlHelperExtensions.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Routing; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo -{ - /// - /// extension methods. - /// - public static class UrlHelperExtensions - { - public static string GetAbsoluteUri(this HttpRequest request) - { - return string.Concat( -request.Scheme, -"://", -request.Host.ToUriComponent(), -request.PathBase.ToUriComponent(), -request.Path.ToUriComponent(), -request.QueryString.ToUriComponent()); - } - - /// - /// Generates a fully qualified URL to an action method by using the specified action name, controller name and - /// route values. - /// - /// The URL helper. - /// The name of the action method. - /// The name of the controller. - /// The route values. - /// The absolute URL. - public static string AbsoluteAction( - this IUrlHelper url, - string actionName, - string controllerName, - object routeValues = null) - { - return url.Action(actionName, controllerName, routeValues, url.ActionContext.HttpContext.Request.Scheme); - } - - /// - /// Generates a fully qualified URL to the specified content by using the specified content path. Converts a - /// virtual (relative) path to an application absolute path. - /// - /// The URL helper. - /// The content path. - /// The absolute URL. - public static string AbsoluteContent( - this IUrlHelper url, - string contentPath) - { - HttpRequest request = url.ActionContext.HttpContext.Request; - return new Uri(new Uri(request.Scheme + "://" + request.Host.Value), url.Content(contentPath)).ToString(); - } - - /// - /// Generates a fully qualified URL to the specified route by using the route name and route values. - /// - /// The URL helper. - /// Name of the route. - /// The route values. - /// The absolute URL. - public static string AbsoluteRouteUrl( - this IUrlHelper url, - string routeName, - object routeValues = null) - { - return url.RouteUrl(routeName, routeValues, url.ActionContext.HttpContext.Request.Scheme); - } - } -} diff --git a/samples/Demo/Migrations/20171228051742_Initial.Designer.cs b/samples/Demo/Migrations/20171228051742_Initial.Designer.cs deleted file mode 100644 index 882a4d8..0000000 --- a/samples/Demo/Migrations/20171228051742_Initial.Designer.cs +++ /dev/null @@ -1,245 +0,0 @@ -// -using Demo.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using System; - -namespace Demo.Migrations -{ - [DbContext(typeof(IdentityDbContext))] - [Migration("20171228051742_Initial")] - partial class Initial - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); - - modelBuilder.Entity("Demo.Models.AppUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AccessFailedCount"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Email") - .HasMaxLength(256); - - b.Property("EmailConfirmed"); - - b.Property("LockoutEnabled"); - - b.Property("LockoutEnd"); - - b.Property("NormalizedEmail") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .HasMaxLength(256); - - b.Property("PasswordHash"); - - b.Property("PhoneNumber"); - - b.Property("PhoneNumberConfirmed"); - - b.Property("SecurityStamp"); - - b.Property("TwoFactorEnabled"); - - b.Property("UserName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasName("UserNameIndex"); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("Demo.Models.ReceivedTextMessage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Content"); - - b.Property("From"); - - b.Property("ReceivedTime"); - - b.Property("To"); - - b.HasKey("Id"); - - b.ToTable("ReceivedTextMessages"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Name") - .HasMaxLength(256); - - b.Property("NormalizedName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasName("RoleNameIndex"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("RoleId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider"); - - b.Property("ProviderKey"); - - b.Property("ProviderDisplayName"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId"); - - b.Property("RoleId"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId"); - - b.Property("LoginProvider"); - - b.Property("Name"); - - b.Property("Value"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Demo/Migrations/20171228051742_Initial.cs b/samples/Demo/Migrations/20171228051742_Initial.cs deleted file mode 100644 index bdb1e48..0000000 --- a/samples/Demo/Migrations/20171228051742_Initial.cs +++ /dev/null @@ -1,236 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; - -namespace Demo.Migrations -{ - public partial class Initial : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AspNetRoles", - columns: table => new - { - Id = table.Column(nullable: false), - ConcurrencyStamp = table.Column(nullable: true), - Name = table.Column(maxLength: 256, nullable: true), - NormalizedName = table.Column(maxLength: 256, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetUsers", - columns: table => new - { - Id = table.Column(nullable: false), - AccessFailedCount = table.Column(nullable: false), - ConcurrencyStamp = table.Column(nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false), - LockoutEnabled = table.Column(nullable: false), - LockoutEnd = table.Column(nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - NormalizedUserName = table.Column(maxLength: 256, nullable: true), - PasswordHash = table.Column(nullable: true), - PhoneNumber = table.Column(nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false), - SecurityStamp = table.Column(nullable: true), - TwoFactorEnabled = table.Column(nullable: false), - UserName = table.Column(maxLength: 256, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ReceivedTextMessages", - columns: table => new - { - Id = table.Column(nullable: false), - Content = table.Column(nullable: true), - From = table.Column(nullable: true), - ReceivedTime = table.Column(nullable: false), - To = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_ReceivedTextMessages", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetRoleClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true), - RoleId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true), - UserId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetUserClaims_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserLogins", - columns: table => new - { - LoginProvider = table.Column(nullable: false), - ProviderKey = table.Column(nullable: false), - ProviderDisplayName = table.Column(nullable: true), - UserId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK_AspNetUserLogins_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserRoles", - columns: table => new - { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserTokens", - columns: table => new - { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(nullable: false), - Name = table.Column(nullable: false), - Value = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AspNetUserTokens_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AspNetRoleClaims_RoleId", - table: "AspNetRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "AspNetRoles", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserClaims_UserId", - table: "AspNetUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserLogins_UserId", - table: "AspNetUserLogins", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserRoles_RoleId", - table: "AspNetUserRoles", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "AspNetUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "AspNetUsers", - column: "NormalizedUserName", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AspNetRoleClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserLogins"); - - migrationBuilder.DropTable( - name: "AspNetUserRoles"); - - migrationBuilder.DropTable( - name: "AspNetUserTokens"); - - migrationBuilder.DropTable( - name: "ReceivedTextMessages"); - - migrationBuilder.DropTable( - name: "AspNetRoles"); - - migrationBuilder.DropTable( - name: "AspNetUsers"); - } - } -} diff --git a/samples/Demo/Migrations/20190320025437_CleanIdentityDb.Designer.cs b/samples/Demo/Migrations/20190320025437_CleanIdentityDb.Designer.cs deleted file mode 100644 index 9dab8bc..0000000 --- a/samples/Demo/Migrations/20190320025437_CleanIdentityDb.Designer.cs +++ /dev/null @@ -1,225 +0,0 @@ -// -using System; -using Demo.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace Demo.Migrations -{ - [DbContext(typeof(IdentityDbContext))] - [Migration("20190320025437_CleanIdentityDb")] - partial class CleanIdentityDb - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.8-servicing-32085"); - - modelBuilder.Entity("Demo.Models.AppUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AccessFailedCount"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Email") - .HasMaxLength(256); - - b.Property("EmailConfirmed"); - - b.Property("LockoutEnabled"); - - b.Property("LockoutEnd"); - - b.Property("NormalizedEmail") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .HasMaxLength(256); - - b.Property("PasswordHash"); - - b.Property("PhoneNumber"); - - b.Property("PhoneNumberConfirmed"); - - b.Property("SecurityStamp"); - - b.Property("TwoFactorEnabled"); - - b.Property("UserName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasName("UserNameIndex"); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Name") - .HasMaxLength(256); - - b.Property("NormalizedName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasName("RoleNameIndex"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("RoleId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider"); - - b.Property("ProviderKey"); - - b.Property("ProviderDisplayName"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId"); - - b.Property("RoleId"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId"); - - b.Property("LoginProvider"); - - b.Property("Name"); - - b.Property("Value"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Demo/Migrations/20190320025437_CleanIdentityDb.cs b/samples/Demo/Migrations/20190320025437_CleanIdentityDb.cs deleted file mode 100644 index 14dc859..0000000 --- a/samples/Demo/Migrations/20190320025437_CleanIdentityDb.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Demo.Migrations -{ - public partial class CleanIdentityDb : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "ReceivedTextMessages"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "ReceivedTextMessages", - columns: table => new - { - Id = table.Column(nullable: false), - Content = table.Column(nullable: true), - From = table.Column(nullable: true), - ReceivedTime = table.Column(nullable: false), - To = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_ReceivedTextMessages", x => x.Id); - }); - } - } -} diff --git a/samples/Demo/Migrations/AppDbContextModelSnapshot.cs b/samples/Demo/Migrations/AppDbContextModelSnapshot.cs deleted file mode 100644 index 6533086..0000000 --- a/samples/Demo/Migrations/AppDbContextModelSnapshot.cs +++ /dev/null @@ -1,223 +0,0 @@ -// -using System; -using Demo.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace Demo.Migrations -{ - [DbContext(typeof(IdentityDbContext))] - partial class AppDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.8-servicing-32085"); - - modelBuilder.Entity("Demo.Models.AppUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AccessFailedCount"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Email") - .HasMaxLength(256); - - b.Property("EmailConfirmed"); - - b.Property("LockoutEnabled"); - - b.Property("LockoutEnd"); - - b.Property("NormalizedEmail") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .HasMaxLength(256); - - b.Property("PasswordHash"); - - b.Property("PhoneNumber"); - - b.Property("PhoneNumberConfirmed"); - - b.Property("SecurityStamp"); - - b.Property("TwoFactorEnabled"); - - b.Property("UserName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasName("UserNameIndex"); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Name") - .HasMaxLength(256); - - b.Property("NormalizedName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasName("RoleNameIndex"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("RoleId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider"); - - b.Property("ProviderKey"); - - b.Property("ProviderDisplayName"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId"); - - b.Property("RoleId"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId"); - - b.Property("LoginProvider"); - - b.Property("Name"); - - b.Property("Value"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Demo.Models.AppUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Demo/Migrations/WeixinDb/20190320025259_AddWeixinSubscribers.Designer.cs b/samples/Demo/Migrations/WeixinDb/20190320025259_AddWeixinSubscribers.Designer.cs deleted file mode 100644 index a53bea6..0000000 --- a/samples/Demo/Migrations/WeixinDb/20190320025259_AddWeixinSubscribers.Designer.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -using System; -using Demo.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace Demo.Migrations.WeixinDb -{ - [DbContext(typeof(WeixinDbContext))] - [Migration("20190320025259_AddWeixinSubscribers")] - partial class AddWeixinSubscribers - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.8-servicing-32085"); - - modelBuilder.Entity("Demo.Models.ReceivedTextMessage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Content"); - - b.Property("From"); - - b.Property("ReceivedTime"); - - b.Property("To"); - - b.HasKey("Id"); - - b.ToTable("ReceivedTextMessages"); - }); - - modelBuilder.Entity("Demo.Models.WeixinSubscriber", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AvatorImage"); - - b.Property("Gender"); - - b.Property("NickName"); - - b.Property("OpenId"); - - b.HasKey("Id"); - - b.ToTable("Subscribers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Demo/Migrations/WeixinDb/20190320025259_AddWeixinSubscribers.cs b/samples/Demo/Migrations/WeixinDb/20190320025259_AddWeixinSubscribers.cs deleted file mode 100644 index c5563d3..0000000 --- a/samples/Demo/Migrations/WeixinDb/20190320025259_AddWeixinSubscribers.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Demo.Migrations.WeixinDb -{ - public partial class AddWeixinSubscribers : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "ReceivedTextMessages", - columns: table => new - { - Id = table.Column(nullable: false), - From = table.Column(nullable: true), - To = table.Column(nullable: true), - ReceivedTime = table.Column(nullable: false), - Content = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_ReceivedTextMessages", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Subscribers", - columns: table => new - { - Id = table.Column(nullable: false), - OpenId = table.Column(nullable: true), - Gender = table.Column(nullable: true), - NickName = table.Column(nullable: true), - AvatorImage = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Subscribers", x => x.Id); - }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "ReceivedTextMessages"); - - migrationBuilder.DropTable( - name: "Subscribers"); - } - } -} diff --git a/samples/Demo/Migrations/WeixinDb/WeixinDbContextModelSnapshot.cs b/samples/Demo/Migrations/WeixinDb/WeixinDbContextModelSnapshot.cs deleted file mode 100644 index b3c0849..0000000 --- a/samples/Demo/Migrations/WeixinDb/WeixinDbContextModelSnapshot.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -using System; -using Demo.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace Demo.Migrations.WeixinDb -{ - [DbContext(typeof(WeixinDbContext))] - partial class WeixinDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.8-servicing-32085"); - - modelBuilder.Entity("Demo.Models.ReceivedTextMessage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Content"); - - b.Property("From"); - - b.Property("ReceivedTime"); - - b.Property("To"); - - b.HasKey("Id"); - - b.ToTable("ReceivedTextMessages"); - }); - - modelBuilder.Entity("Demo.Models.WeixinSubscriber", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AvatorImage"); - - b.Property("Gender"); - - b.Property("NickName"); - - b.Property("OpenId"); - - b.HasKey("Id"); - - b.ToTable("Subscribers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Demo/Models/Home/IndexViewModel.cs b/samples/Demo/Models/Home/IndexViewModel.cs deleted file mode 100644 index 265068a..0000000 --- a/samples/Demo/Models/Home/IndexViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Models.Home -{ - public class IndexViewModel - { - public int SubscriberCount { get; set; } - public int ReceivedTextCount { get; set; } - } -} diff --git a/samples/Demo/Models/Home/SendWeixinViewModel.cs b/samples/Demo/Models/Home/SendWeixinViewModel.cs deleted file mode 100644 index ea91072..0000000 --- a/samples/Demo/Models/Home/SendWeixinViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Models -{ - public class SendWeixinViewModel - { - public IList Received { get; set; } - - [Required] - public string OpenId { get; set; } - - [Required] - public string Content { get; set; } - } -} diff --git a/samples/Demo/Models/Weixin/JweixinViewModel.cs b/samples/Demo/Models/Weixin/JweixinViewModel.cs deleted file mode 100644 index e04e5fe..0000000 --- a/samples/Demo/Models/Weixin/JweixinViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using AspNetCore.Weixin; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Models -{ - public class JweixinViewModel - { - public string ConfigJson { get; set; } - } -} diff --git a/samples/Demo/Models/Weixin/ShareJweixinViewModel.cs b/samples/Demo/Models/Weixin/ShareJweixinViewModel.cs deleted file mode 100644 index 03c2c81..0000000 --- a/samples/Demo/Models/Weixin/ShareJweixinViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Models -{ - public class ShareJweixinViewModel : JweixinViewModel - { - public string Url { get; set; } - public string Title { get; set; } - public string Description { get; set; } - public string ImgUrl { get; set; } - } -} diff --git a/samples/Demo/Models/Weixin/WeixinJsonViewModel.cs b/samples/Demo/Models/Weixin/WeixinJsonViewModel.cs deleted file mode 100644 index f581587..0000000 --- a/samples/Demo/Models/Weixin/WeixinJsonViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace Demo.Models -{ - - public class WeixinJsonViewModel - { - public string AppId { get; set; } - - public string Token { get; set; } - - [MaxLength(102400)] - public string Json { get; set; } - } -} diff --git a/samples/Demo/Program.cs b/samples/Demo/Program.cs deleted file mode 100644 index 737f2b1..0000000 --- a/samples/Demo/Program.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Demo.Data; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace Demo -{ - public class Program - { - public static void Main(string[] args) - { - BuildWebHost(args) - .MigrateDatabase() - .SeedDatabase() - .Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(ConfigureAppConfiguration) - .ConfigureLogging(ConfigureLogging) - .UseStartup() - .Build(); - - private static void ConfigureAppConfiguration(WebHostBuilderContext hostingContext, IConfigurationBuilder config) - { - var env = hostingContext.HostingEnvironment; - var environmentName = env.EnvironmentName; - - //1。使用默认配置文件。源码可见。通常直接在字段中填写配置说明。 - //2。使用secret.json。通常,不论是在开发者个人机,测试服务器,还是正式部署的服务器上,应当使用此配置文件。 - //3。在数据库开发者的个人机上,通常需要在Development和Production两种模式中频繁切换,此时我们可以创建x.Development.json来替换secret.json中的配置。 - //4。除了Development具有替换secret.json的能力,其他Environment也可能需要这种替换能力。 - config.SetBasePath(Directory.GetCurrentDirectory()) - //.SetBasePath(AppContext.BaseDirectory) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false) - .AddUserSecrets() - .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true); - } - - private static void ConfigureLogging(WebHostBuilderContext hostingContext, ILoggingBuilder logging) - { - logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - logging.AddConsole(); - logging.AddDebug(); - } - - private static void DefaultServiceProvider(WebHostBuilderContext hostingContext, Microsoft.Extensions.DependencyInjection.ServiceProviderOptions options) - { - // To detect: InvalidOperationException: Cannot consume scoped service 'Ruhu.AppDbContext' from singleton 'Microsoft.AspNetCore.Authorization.IAuthorizationHandler'. - options.ValidateScopes = hostingContext.HostingEnvironment.IsDevelopment(); - } - } -} diff --git a/samples/Demo/Properties/launchSettings.json b/samples/Demo/Properties/launchSettings.json deleted file mode 100644 index cfb34d0..0000000 --- a/samples/Demo/Properties/launchSettings.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:6606", - "sslPort": 0 - } - }, - "profiles": { - "Development": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Production": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Production" - } - } - } -} \ No newline at end of file diff --git a/samples/Demo/Startup.cs b/samples/Demo/Startup.cs deleted file mode 100644 index 83c0e2a..0000000 --- a/samples/Demo/Startup.cs +++ /dev/null @@ -1,165 +0,0 @@ -using Myvas.AspNetCore.TencentSms; -using AspNetCore.Weixin; -using Demo.Applications; -using Demo.Data; -using Demo.Entities; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Http; -using System; - -namespace Demo -{ - public class Startup - { - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.Configure(options => - { - // This lambda determines whether user consent for non-essential cookies is needed for a given request. - options.CheckConsentNeeded = context => true; - options.MinimumSameSitePolicy = SameSiteMode.None; - }); - - services.AddDbContext(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); - services.AddDbContext(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); - - services.Configure(options => - { - options.Password = new PasswordOptions - { - RequireLowercase = false, - RequireUppercase = false, - RequireNonAlphanumeric = false, - RequireDigit = false - }; - options.User.RequireUniqueEmail = false; - options.SignIn.RequireConfirmedEmail = false; - - options.SignIn.RequireConfirmedPhoneNumber = true; - }); - services.ConfigureApplicationCookie(options => - { - // Cookie settings - options.Cookie.HttpOnly = true; - options.Cookie.Expiration = TimeSpan.FromDays(150); - // If the LoginPath isn't set, ASP.NET Core defaults - // the path to /Account/Login. - options.LoginPath = "/Account/Login"; - options.LogoutPath = "/Account/LogOff"; - // If the AccessDeniedPath isn't set, ASP.NET Core defaults - // the path to /Account/AccessDenied. - options.AccessDeniedPath = "/Account/AccessDenied"; - options.SlidingExpiration = true; - }); - services.AddIdentity() - .AddEntityFrameworkStores() - .AddUserManager() - .AddSignInManager>() - .AddDefaultTokenProviders() - .AddDefaultUI(); - services.AddAuthentication() - .AddWeixinOpen(options => - { - options.AppId = Configuration["WeixinOpen:AppId"]; - options.AppSecret = Configuration["WeixinOpen:AppSecret"]; - }) - .AddWeixinAuth(options => - { - options.AppId = Configuration["WeixinAuth:AppId"]; - options.AppSecret = Configuration["WeixinAuth:AppSecret"]; - }); - services.AddTencentSms(options => - { - options.SdkAppId = Configuration["QcloudSms:SdkAppId"]; - options.AppKey = Configuration["QcloudSms:AppKey"]; - }); - services.AddViewDivert(); - - services.AddWeixinAccessToken(options => - { - options.AppId = Configuration["Weixin:AppId"]; - options.AppSecret = Configuration["Weixin:AppSecret"]; - }); - services.AddWeixinJssdk(options => - { - options.AppId = Configuration["Weixin:AppId"]; - }); - services.AddScoped(); - var weixinEventSink = services.BuildServiceProvider().GetRequiredService(); - services.AddWeixinWelcomePage(options => - { - options.AppId = Configuration["Weixin:AppId"]; - options.AppSecret = Configuration["Weixin:AppSecret"]; - options.WebsiteToken = Configuration["Weixin:WebsiteToken"]; - options.EncodingAESKey = Configuration["Weixin:EncodingAESKey"]; - options.Path = "/wx"; - options.Events = new WeixinMessageEvents() - { - OnTextMessageReceived = ctx => weixinEventSink.OnTextMessageReceived(ctx.Sender, ctx.Args), - OnLinkMessageReceived = ctx => weixinEventSink.OnLinkMessageReceived(ctx.Sender, ctx.Args), - OnClickMenuEventReceived = ctx => weixinEventSink.OnClickMenuEventReceived(ctx.Sender, ctx.Args), - OnImageMessageReceived = ctx => weixinEventSink.OnImageMessageReceived(ctx.Sender, ctx.Args), - OnLocationEventReceived = ctx => weixinEventSink.OnLocationEventReceived(ctx.Sender, ctx.Args), - OnLocationMessageReceived = ctx => weixinEventSink.OnLocationMessageReceived(ctx.Sender, ctx.Args), - OnQrscanEventReceived = ctx => weixinEventSink.OnQrscanEventReceived(ctx.Sender, ctx.Args), - OnSubscribeEventReceived = ctx => weixinEventSink.OnSubscribeEventReceived(ctx.Sender, ctx.Args), - OnUnsubscribeEventReceived = ctx => weixinEventSink.OnUnsubscribeEventReceived(ctx.Sender, ctx.Args), - OnVideoMessageReceived = ctx => weixinEventSink.OnVideoMessageReceived(ctx.Sender, ctx.Args), - OnShortVideoMessageReceived = ctx => weixinEventSink.OnShortVideoMessageReceived(ctx.Sender, ctx.Args), - OnViewMenuEventReceived = ctx => weixinEventSink.OnViewMenuEventReceived(ctx.Sender, ctx.Args), - OnVoiceMessageReceived = ctx => weixinEventSink.OnVoiceMessageReceived(ctx.Sender, ctx.Args) - }; - }); - - services.AddMvc() - .SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1); - - services.AddAuthorization(options => - { - options.AddPolicy("WeixinMenuManager", policy => policy.RequireUserName(IdentityDbInitializer.AdminUserName)); - }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - //app.UseHsts(); - } - - //app.UseHttpsRedirection(); - app.UseStaticFiles(); - //app.UseCookiePolicy(); - - app.UseAuthentication(); - - app.UseWeixinWelcomePage(); - - app.UseMvc(routes => - { - routes.MapRoute( - name: "default", - template: "{controller=Home}/{action=Index}/{id?}"); - }); - } - } -} \ No newline at end of file diff --git a/samples/Demo/Views/Home/About.cshtml b/samples/Demo/Views/Home/About.cshtml deleted file mode 100644 index 429ce98..0000000 --- a/samples/Demo/Views/Home/About.cshtml +++ /dev/null @@ -1,12 +0,0 @@ - -@{ - ViewData["Title"] = "About"; -} - -

@ViewData["Title"]

- - -AspNetCore.Weixin -AspnetCore.Weixin.Jssdk - - diff --git a/samples/Demo/Views/Home/Index.cshtml b/samples/Demo/Views/Home/Index.cshtml deleted file mode 100644 index c0cc63a..0000000 --- a/samples/Demo/Views/Home/Index.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@model Demo.Models.Home.IndexViewModel -@{ - ViewData["Title"] = "Index"; -} - -
-
-
- -
请用微信扫描二维码关注测试公众号
-
- - -
-
- -
 
-
-
-
-

© 2016-2019 - Myvas.AspNetCore.Weixin

-
-
\ No newline at end of file diff --git a/samples/Demo/Views/Home/ReceivedText.cshtml b/samples/Demo/Views/Home/ReceivedText.cshtml deleted file mode 100644 index 819123f..0000000 --- a/samples/Demo/Views/Home/ReceivedText.cshtml +++ /dev/null @@ -1,45 +0,0 @@ -@model IEnumerable - -@{ - ViewData["Title"] = "Received Text"; -} - -

@ViewData["Title"] (@Model.Count())

- - -@foreach (var item in Model) -{ -
- @**@ -
- @*
- @item.From - @{ - switch (item.sex) - { - case 1: - - break; - case 2: - - break; - default: - break; - } - } -
*@ -
-
OpenId
-
-
@item.From
-
-
Content
-
@item.Content
-
Received Time
-
@item.ReceivedTime.ToLocalTime().ToString("yyyy-MM-dd hh:mm:ss")
-
- 发送微信消息 -
-
-
-} \ No newline at end of file diff --git a/samples/Demo/Views/Home/SendWeixin.cshtml b/samples/Demo/Views/Home/SendWeixin.cshtml deleted file mode 100644 index 14b41ff..0000000 --- a/samples/Demo/Views/Home/SendWeixin.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@model SendWeixinViewModel - -@{ - ViewData["Title"] = "Send Weixin Message"; -} - -

@ViewData["Title"]

- -
- @Html.AntiForgeryToken() -
-
- openid -
-
- -
-
- content -
-
- -
-
- -
\ No newline at end of file diff --git a/samples/Demo/Views/Home/Subscribers.cshtml b/samples/Demo/Views/Home/Subscribers.cshtml deleted file mode 100644 index 68be68d..0000000 --- a/samples/Demo/Views/Home/Subscribers.cshtml +++ /dev/null @@ -1,53 +0,0 @@ -@model ReturnableViewModel> - -@{ - ViewData["Title"] = "Subscribers"; -} - -

@ViewData["Title"]

- -@if (Model.Item == null) -{ -

No items found.

-} -else -{ - @foreach (var item in Model.Item) - { -
- -
-
- @item.nickname - @{ - switch (item.sex) - { - case 1: - - break; - case 2: - - break; - default: - break; - } - } -
-
-
OpenId
-
-
@item.openid
-
-
Province
-
@item.province
-
City
-
@item.city
-
Subscribe Time
-
@AspNetCore.Weixin.WeixinTimestampHelper.ToLocalTime(item.subscribe_time)
-
- 发送微信消息 -
-
-
- } -} \ No newline at end of file diff --git a/samples/Demo/Views/Jssdk/Index.cshtml b/samples/Demo/Views/Jssdk/Index.cshtml deleted file mode 100644 index 541fd8b..0000000 --- a/samples/Demo/Views/Jssdk/Index.cshtml +++ /dev/null @@ -1,46 +0,0 @@ -@model ShareJweixinViewModel -@{ - Layout = "_LayoutJweixin"; - ViewData["Title"] = "Index"; -} - -

Index

- -
Check JsApi
- -@section Scripts{ - -} \ No newline at end of file diff --git a/samples/Demo/Views/Shared/Error.cshtml b/samples/Demo/Views/Shared/Error.cshtml deleted file mode 100644 index 6c6d047..0000000 --- a/samples/Demo/Views/Shared/Error.cshtml +++ /dev/null @@ -1,14 +0,0 @@ -@{ - ViewData["Title"] = "Error"; -} - -

Error.

-

An error occurred while processing your request.

- -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that occurred. -

-

- Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. -

diff --git a/samples/Demo/Views/Shared/_Layout.cshtml b/samples/Demo/Views/Shared/_Layout.cshtml deleted file mode 100644 index 17aea55..0000000 --- a/samples/Demo/Views/Shared/_Layout.cshtml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - @ViewData["Title"] - Demo - - - - - - - - - - - - -
- @RenderBody() -
- - - - - - - - - - - @RenderSection("scripts", required: false) - - diff --git a/samples/Demo/Views/Shared/_LayoutJweixin.cshtml b/samples/Demo/Views/Shared/_LayoutJweixin.cshtml deleted file mode 100644 index 504c4b9..0000000 --- a/samples/Demo/Views/Shared/_LayoutJweixin.cshtml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - @ViewData["Title"] - AspNetCore.Weixin Demo - - - - - - - - - - - - -
- @RenderBody() -
- - - - - - - - - - - - @RenderSection("scripts", required: false) - - diff --git a/samples/Demo/Views/Shared/_LoginPartial.cshtml b/samples/Demo/Views/Shared/_LoginPartial.cshtml deleted file mode 100644 index 3d8db48..0000000 --- a/samples/Demo/Views/Shared/_LoginPartial.cshtml +++ /dev/null @@ -1,23 +0,0 @@ -@inject Microsoft.AspNetCore.Identity.SignInManager SignInManager -@inject Microsoft.AspNetCore.Identity.UserManager UserManager - -@if (SignInManager.IsSignedIn(User)) -{ - -} -else -{ - -} diff --git a/samples/Demo/Views/WeixinMenu/Index.cshtml b/samples/Demo/Views/WeixinMenu/Index.cshtml deleted file mode 100644 index 0adaa4b..0000000 --- a/samples/Demo/Views/WeixinMenu/Index.cshtml +++ /dev/null @@ -1,57 +0,0 @@ -@* - WeixinMenu.cshtml -*@ - -@model WeixinJsonViewModel - -@{ - ViewData["Title"] = "微信菜单管理"; -} - - -
-
-
WeixinAccessToken
-
-
@Model.Token
- - -
-
-
- -
-
-
- - -
-
- - -
-
-
- -
-
- -
-

菜单获取的Json格式与菜单更新的Json不一样,请手动复制以下内容粘贴到[新菜单]输入框,然后点击提交。

-

-{
-	"button": [
-	{
-			"type": "view",
-			"name": "Home",
-			"url": "http://weixin.myvas.com/"
-	},
-	{
-			"type": "view",
-			"name": "About",
-			"url": "http://weixin.myvas.com/Home/About"
-		
-	}]
-}
-    
-
\ No newline at end of file diff --git a/samples/Demo/Views/WeixinMenu/UpdateMenuResult.cshtml b/samples/Demo/Views/WeixinMenu/UpdateMenuResult.cshtml deleted file mode 100644 index c886039..0000000 --- a/samples/Demo/Views/WeixinMenu/UpdateMenuResult.cshtml +++ /dev/null @@ -1,9 +0,0 @@ -@* - 微信菜单管理 -*@ -@model AspNetCore.Weixin.WeixinErrorJson -@{ - ViewData["Title"] = "微信菜单更新结果"; -} - -

[@Model.errcode] @Model.errmsg

diff --git a/samples/Demo/Views/_ViewImports.cshtml b/samples/Demo/Views/_ViewImports.cshtml deleted file mode 100644 index 9b7fc08..0000000 --- a/samples/Demo/Views/_ViewImports.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -@using Demo -@using Demo.Entities -@using Demo.Models -@using AspNetCore.Weixin - -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers \ No newline at end of file diff --git a/samples/Demo/Views/_ViewStart.cshtml b/samples/Demo/Views/_ViewStart.cshtml deleted file mode 100644 index 6e88aa3..0000000 --- a/samples/Demo/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "_Layout"; -} diff --git a/samples/Demo/appsettings.json b/samples/Demo/appsettings.json deleted file mode 100644 index a6d8f86..0000000 --- a/samples/Demo/appsettings.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "ConnectionStrings:DefaultConnection": "Data Source=wxdemo.sqlite", - - "Weixin:AppId": "your appid of mp.weixin.qq.com account", - "Weixin:AppSecret": "your appsecret of mp.weixin.qq.com account", - "Weixin:WebsiteToken": "your token of website configuration in mp.weixin.qq.com", - "Weixin:EncodingAESKey": "your EncodingAESKey of message encryption configured in mp.weixin.qq.com", - - "WeixinOAuth:AppId": "your appid of mp.weixin.qq.com account", - "WeixinOAuth:AppSecret": "your appsecret of mp.weixin.qq.com account", - - "WeixinOpen:AppId": "your appid of open.weixin.qq.com account", - "WeixinOpen:AppSecret": "your appsecret of open.weixin.qq.com account", - - "QcloudSms:SdkAppId": "your SdkAppId of Qcloud SMS", - "QcloudSms:AppKey": "your AppKey of Qcloud SMS", - - "Logging": { - "IncludeScopes": true, - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - }, - "Debug": { - "LogLevel": { - "Default": "Trace" - } - }, - "Console": { - "LogLevel": { - "Microsoft.EntityFrameworkCore.Database.Connection": "Debug", - "Microsoft.EntityFrameworkCore.Database.Command": "Debug", - "Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning", - "Microsoft.AspNetCore.Mvc.Razor.Razor": "Warning", - "Microsoft.AspNetCore.Mvc.Razor": "Error", - "Default": "Debug" - } - } - } -} diff --git a/samples/Demo/bundleconfig.json b/samples/Demo/bundleconfig.json deleted file mode 100644 index a98e0c9..0000000 --- a/samples/Demo/bundleconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -// Configure bundling and minification for the project. -// More info at https://go.microsoft.com/fwlink/?LinkId=808241 -[ - { - "outputFileName": "wwwroot/css/site.min.css", - // An array of relative input file paths. Globbing patterns supported - "inputFiles": [ - "wwwroot/css/site.css" - ] - }, - { - "outputFileName": "wwwroot/js/site.min.js", - "inputFiles": [ - "wwwroot/js/site.js" - ], - // Optionally specify minification options - "minify": { - "enabled": true, - "renameLocals": true - }, - // Optinally generate .map file - "sourceMap": false - } -] diff --git a/samples/Demo/libman.json b/samples/Demo/libman.json deleted file mode 100644 index 43c27d3..0000000 --- a/samples/Demo/libman.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "version": "1.0", - "defaultProvider": "cdnjs", - "libraries": [ - { - "library": "jquery@3.3.1", - "destination": "wwwroot/lib/jquery/", - "files": [ - "jquery.min.js" - ] - }, - { - "provider": "cdnjs", - "library": "jquery-validate@1.19.0", - "destination": "wwwroot/lib/jquery-validate/", - "files": [ - "jquery.validate.min.js" - ] - }, - { - "provider": "cdnjs", - "library": "jquery-validation-unobtrusive@3.2.11", - "destination": "wwwroot/lib/jquery-validation-unobtrusive/", - "files": [ - "jquery.validate.unobtrusive.min.js" - ] - }, - { - "provider": "cdnjs", - "library": "twitter-bootstrap@4.3.1", - "destination": "wwwroot/lib/twitter-bootstrap/", - "files": [ - "js/bootstrap.bundle.min.js", - "css/bootstrap.min.css" - ] - }, - { - "provider": "cdnjs", - "library": "font-awesome@5.7.2", - "destination": "wwwroot/lib/font-awesome/", - "files": [ - "webfonts/fa-solid-900.eot", - "webfonts/fa-solid-900.ttf", - "webfonts/fa-solid-900.svg", - "webfonts/fa-solid-900.woff", - "webfonts/fa-solid-900.woff2", - "css/all.min.css" - ] - } - ] -} \ No newline at end of file diff --git a/samples/Demo/web.config b/samples/Demo/web.config deleted file mode 100644 index 2074105..0000000 --- a/samples/Demo/web.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - diff --git a/samples/Demo/wwwroot/MP_verify_MQuINXpToN5ZYtjc.txt b/samples/Demo/wwwroot/MP_verify_MQuINXpToN5ZYtjc.txt deleted file mode 100644 index 5b68eeb..0000000 --- a/samples/Demo/wwwroot/MP_verify_MQuINXpToN5ZYtjc.txt +++ /dev/null @@ -1 +0,0 @@ -MQuINXpToN5ZYtjc \ No newline at end of file diff --git a/samples/Demo/wwwroot/css/site.css b/samples/Demo/wwwroot/css/site.css deleted file mode 100644 index 3507b5b..0000000 --- a/samples/Demo/wwwroot/css/site.css +++ /dev/null @@ -1,43 +0,0 @@ -body { - padding-top: 50px; - padding-bottom: 20px; -} - -/* Set padding to keep content from hitting the edges */ -.body-content { - padding-left: 15px; - padding-right: 15px; -} - -/* Set width on the form input elements since they're 100% wide by default */ -input, -select, -textarea { - max-width: 280px; -} - -/* styles for validation helpers */ -.field-validation-error { - color: #b94a48; -} - -.field-validation-valid { - display: none; -} - -input.input-validation-error { - border: 1px solid #b94a48; -} - -input[type="checkbox"].input-validation-error { - border: 0 none; -} - -.validation-summary-errors { - color: #b94a48; -} - -.validation-summary-valid { - display: none; -} - diff --git a/samples/Demo/wwwroot/img/mp-test.jpg b/samples/Demo/wwwroot/img/mp-test.jpg deleted file mode 100644 index fbb146c..0000000 Binary files a/samples/Demo/wwwroot/img/mp-test.jpg and /dev/null differ diff --git a/samples/Demo/wwwroot/js/site.js b/samples/Demo/wwwroot/js/site.js deleted file mode 100644 index 947bbcf..0000000 --- a/samples/Demo/wwwroot/js/site.js +++ /dev/null @@ -1,5 +0,0 @@ -// Write your Javascript code. - -$(function () { - $('[data-toggle="popover"]').popover() -}) \ No newline at end of file diff --git a/samples/Demo/wxdemo.sqlite b/samples/Demo/wxdemo.sqlite deleted file mode 100644 index 8a53ac4..0000000 Binary files a/samples/Demo/wxdemo.sqlite and /dev/null differ diff --git a/samples/db-schema-update-20190320.sql b/samples/db-schema-update-20190320.sql deleted file mode 100644 index a22abdf..0000000 --- a/samples/db-schema-update-20190320.sql +++ /dev/null @@ -1,87 +0,0 @@ -BEGIN TRANSACTION; -CREATE TABLE "__EFMigrationsHistory" ( - "MigrationId" TEXT NOT NULL CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY, - "ProductVersion" TEXT NOT NULL -); -CREATE TABLE "Subscribers" ( - "Id" TEXT NOT NULL CONSTRAINT "PK_Subscribers" PRIMARY KEY, - "OpenId" TEXT NULL, - "Gender" TEXT NULL, - "NickName" TEXT NULL, - "AvatorImage" TEXT NULL -); -CREATE TABLE "ReceivedTextMessages" ( - "Id" TEXT NOT NULL CONSTRAINT "PK_ReceivedTextMessages" PRIMARY KEY, - "From" TEXT NULL, - "To" TEXT NULL, - "ReceivedTime" TEXT NOT NULL, - "Content" TEXT NULL -); -CREATE TABLE "AspNetUsers" ( - "Id" TEXT NOT NULL CONSTRAINT "PK_AspNetUsers" PRIMARY KEY, - "AccessFailedCount" INTEGER NOT NULL, - "ConcurrencyStamp" TEXT NULL, - "Email" TEXT NULL, - "EmailConfirmed" INTEGER NOT NULL, - "LockoutEnabled" INTEGER NOT NULL, - "LockoutEnd" TEXT NULL, - "NormalizedEmail" TEXT NULL, - "NormalizedUserName" TEXT NULL, - "PasswordHash" TEXT NULL, - "PhoneNumber" TEXT NULL, - "PhoneNumberConfirmed" INTEGER NOT NULL, - "SecurityStamp" TEXT NULL, - "TwoFactorEnabled" INTEGER NOT NULL, - "UserName" TEXT NULL -); -CREATE TABLE "AspNetUserTokens" ( - "UserId" TEXT NOT NULL, - "LoginProvider" TEXT NOT NULL, - "Name" TEXT NOT NULL, - "Value" TEXT NULL, - CONSTRAINT "PK_AspNetUserTokens" PRIMARY KEY ("UserId", "LoginProvider", "Name"), - CONSTRAINT "FK_AspNetUserTokens_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE -); -CREATE TABLE "AspNetUserRoles" ( - "UserId" TEXT NOT NULL, - "RoleId" TEXT NOT NULL, - CONSTRAINT "PK_AspNetUserRoles" PRIMARY KEY ("UserId", "RoleId"), - CONSTRAINT "FK_AspNetUserRoles_AspNetRoles_RoleId" FOREIGN KEY ("RoleId") REFERENCES "AspNetRoles" ("Id") ON DELETE CASCADE, - CONSTRAINT "FK_AspNetUserRoles_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE -); -CREATE TABLE "AspNetUserLogins" ( - "LoginProvider" TEXT NOT NULL, - "ProviderKey" TEXT NOT NULL, - "ProviderDisplayName" TEXT NULL, - "UserId" TEXT NOT NULL, - CONSTRAINT "PK_AspNetUserLogins" PRIMARY KEY ("LoginProvider", "ProviderKey"), - CONSTRAINT "FK_AspNetUserLogins_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE -); -CREATE TABLE "AspNetUserClaims" ( - "Id" INTEGER NOT NULL CONSTRAINT "PK_AspNetUserClaims" PRIMARY KEY AUTOINCREMENT, - "ClaimType" TEXT NULL, - "ClaimValue" TEXT NULL, - "UserId" TEXT NOT NULL, - CONSTRAINT "FK_AspNetUserClaims_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE -); -CREATE TABLE "AspNetRoles" ( - "Id" TEXT NOT NULL CONSTRAINT "PK_AspNetRoles" PRIMARY KEY, - "ConcurrencyStamp" TEXT NULL, - "Name" TEXT NULL, - "NormalizedName" TEXT NULL -); -CREATE TABLE "AspNetRoleClaims" ( - "Id" INTEGER NOT NULL CONSTRAINT "PK_AspNetRoleClaims" PRIMARY KEY AUTOINCREMENT, - "ClaimType" TEXT NULL, - "ClaimValue" TEXT NULL, - "RoleId" TEXT NOT NULL, - CONSTRAINT "FK_AspNetRoleClaims_AspNetRoles_RoleId" FOREIGN KEY ("RoleId") REFERENCES "AspNetRoles" ("Id") ON DELETE CASCADE -); -CREATE UNIQUE INDEX "UserNameIndex" ON "AspNetUsers" ("NormalizedUserName"); -CREATE UNIQUE INDEX "RoleNameIndex" ON "AspNetRoles" ("NormalizedName"); -CREATE INDEX "IX_AspNetUserRoles_RoleId" ON "AspNetUserRoles" ("RoleId"); -CREATE INDEX "IX_AspNetUserLogins_UserId" ON "AspNetUserLogins" ("UserId"); -CREATE INDEX "IX_AspNetUserClaims_UserId" ON "AspNetUserClaims" ("UserId"); -CREATE INDEX "IX_AspNetRoleClaims_RoleId" ON "AspNetRoleClaims" ("RoleId"); -CREATE INDEX "EmailIndex" ON "AspNetUsers" ("NormalizedEmail"); -COMMIT; diff --git a/samples/wxdemo.sqlite b/samples/wxdemo.sqlite deleted file mode 100644 index 4356099..0000000 Binary files a/samples/wxdemo.sqlite and /dev/null differ diff --git a/src/Weixin.Jssdk/Myvas.AspNetCore.Weixin.Jssdk.csproj b/src/Weixin.Jssdk/Myvas.AspNetCore.Weixin.Jssdk.csproj index 1efd0b1..3c69d28 100644 --- a/src/Weixin.Jssdk/Myvas.AspNetCore.Weixin.Jssdk.csproj +++ b/src/Weixin.Jssdk/Myvas.AspNetCore.Weixin.Jssdk.csproj @@ -14,7 +14,7 @@ https://github.com/myvas/AspNetCore.Weixin - - + + \ No newline at end of file diff --git a/src/Weixin/AccessToken/WeixinAccessTokenOptions.cs b/src/Weixin/AccessToken/WeixinAccessTokenOptions.cs index d55c066..8718b5f 100644 --- a/src/Weixin/AccessToken/WeixinAccessTokenOptions.cs +++ b/src/Weixin/AccessToken/WeixinAccessTokenOptions.cs @@ -1,6 +1,6 @@ -namespace Myvas.AspNetCore.Weixin -{ - public class WeixinAccessTokenOptions : WeixinOptions - { - } -} +namespace Myvas.AspNetCore.Weixin +{ + public class WeixinAccessTokenOptions : WeixinOptions + { + } +} diff --git a/src/Weixin/Apis/Menu/MenuApi.cs b/src/Weixin/Apis/Menu/MenuApi.cs index e302546..21c5457 100644 --- a/src/Weixin/Apis/Menu/MenuApi.cs +++ b/src/Weixin/Apis/Menu/MenuApi.cs @@ -1,348 +1,349 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text.Json; -using System.Threading.Tasks; - -namespace Myvas.AspNetCore.Weixin -{ - public static class MenuApi - { - ///// - ///// 特殊符号转义 - ///// - ///// - ///// - //private static string ButtonNameEncode(string name) - //{ - // //直接用UrlEncode不行,显示内容超长 - // return name.Replace("&", "%26"); - //} - - /// - /// 创建菜单 - /// - /// - /// 菜单内容 - /// - public static async Task CreateMenu(string accessToken, ButtonGroup buttonData) - { - var api = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}"; - api = string.Format(api, accessToken); - - - ////对特殊符号进行URL转义 - //foreach (var button in buttonData.button) - //{ - // button.name = ButtonNameEncode(button.name);//button.name.UrlEncode(); - // if (button is SubButton) - // { - // var subButtonList = button as SubButton; - // foreach (var subButton in subButtonList.sub_button) - // { - // subButton.name = ButtonNameEncode(button.name);//button.name.UrlEncode(); - // } - // } - //} - - var tokenRequestParameters = new Dictionary() - { - //{ "client_id", Options.ClientId }, - //{ "redirect_uri", redirectUri }, - //{ "client_secret", Options.ClientSecret }, - //{ "code", code }, - //{ "grant_type", "authorization_code" }, - }; - var requestContent = new FormUrlEncodedContent(tokenRequestParameters); - - var requestMessage = new HttpRequestMessage(HttpMethod.Post, api); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - requestMessage.Content = requestContent; - - var response = await new HttpClient().SendAsync(requestMessage); - response.EnsureSuccessStatusCode(); - - var json = await response.Content.ReadAsStringAsync(); - return json.FromJson(); - } - - /// - /// 创建菜单 - /// - /// - /// 菜单内容 - /// - public static async Task CreateMenuAsync(string accessToken, string menuJson) - { - var api = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}"; - api = string.Format(api, accessToken); - - var requestContent = new StringContent(menuJson); - - var requestMessage = new HttpRequestMessage(HttpMethod.Post, api); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - requestMessage.Content = requestContent; - - var response = await new HttpClient().SendAsync(requestMessage); - response.EnsureSuccessStatusCode(); - - var json = await response.Content.ReadAsStringAsync(); - return json.FromJson(); - } - - - #region GetMenu - /// - /// 获取单击按钮 - /// - /// - /// - [Obsolete("配合GetMenuFromJson方法使用")] - private static ClickMenuButton GetSingleButtonFromJsonObject(Dictionary objs) - { - var sb = new ClickMenuButton() - { - key = objs["key"] as string, - name = objs["name"] as string, - type = objs["UploadMediaType"] as string - }; - return sb; - } - - - /// - /// 从JSON字符串获取菜单对象 - /// - /// - /// - [Obsolete("此方法通过判断GetMenuResult并结合object类型转换得到结果。结果准确。但更推荐使用GetMenuFromJsonResult方法。")] - public static GetMenuResult GetMenuFromJson(string json) - { - var finalResult = new GetMenuResult(); - - try - { - //@"{""menu"":{""button"":[{""UploadMediaType"":""click"",""name"":""单击测试"",""key"":""OneClick"",""sub_button"":[]},{""name"":""二级菜单"",""sub_button"":[{""UploadMediaType"":""click"",""name"":""返回文本"",""key"":""SubClickRoot_Text"",""sub_button"":[]},{""UploadMediaType"":""click"",""name"":""返回图文"",""key"":""SubClickRoot_News"",""sub_button"":[]},{""UploadMediaType"":""click"",""name"":""返回音乐"",""key"":""SubClickRoot_Music"",""sub_button"":[]}]}]}}" - object jsonResult = null; - - jsonResult = JsonSerializer.Deserialize(json); - - var fullResult = jsonResult as Dictionary; - if (fullResult != null && fullResult.ContainsKey("menu")) - { - //得到菜单 - var menu = fullResult["menu"]; - var buttons = (menu as Dictionary)["button"] as object[]; - - foreach (var rootButton in buttons) - { - var fullButton = rootButton as Dictionary; - if (fullButton.ContainsKey("key") && !string.IsNullOrEmpty(fullButton["key"] as string)) - { - //按钮,无下级菜单 - finalResult.menu.button.Add(GetSingleButtonFromJsonObject(fullButton)); - } - else - { - //二级菜单 - var subButton = new SubMenuButton(fullButton["name"] as string); - finalResult.menu.button.Add(subButton); - foreach (var sb in fullButton["sub_button"] as object[]) - { - subButton.sub_button.Add(GetSingleButtonFromJsonObject(sb as Dictionary)); - } - } - } - } - else if (fullResult != null && fullResult.ContainsKey("errmsg")) - { - //菜单不存在 - throw new WeixinException(fullResult["errmsg"] as string, null, null); - } - } - catch (WeixinException) - { - finalResult = null; - - //如果没有惨淡会返回错误代码:46003:menu no exist - } - catch (Exception) - { - //其他异常 - finalResult = null; - } - return finalResult; - } - - - /// - /// 获取当前菜单,如果菜单不存在,将返回null - /// - /// - /// - public static async Task GetMenuAsync(string accessToken) - { - var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", accessToken); - - var responseMessage = await new HttpClient().GetAsync(url); - responseMessage.EnsureSuccessStatusCode(); - var s = await responseMessage.Content.ReadAsStringAsync(); - var json = JsonDocument.Parse(s); - return json; - } - - public static GetMenuResult Parse(string json) - { - GetMenuResult finalResult; - try - { - var jsonResult = JsonSerializer.Deserialize(json); - if (jsonResult.menu == null || jsonResult.menu.button.Count == 0) - { - throw new WeixinException(jsonResult.errmsg); - } - - finalResult = GetMenuFromJsonResult(jsonResult); - } - catch (WeixinException) - { - finalResult = null; - } - - return finalResult; - } - - /// - /// 根据微信返回的Json数据得到可用的GetMenuResult结果 - /// - /// - /// - public static GetMenuResult GetMenuFromJsonResult(GetMenuResultFull resultFull) - { - GetMenuResult result = null; - try - { - //重新整理按钮信息 - ButtonGroup bg = new ButtonGroup(); - foreach (var rootButton in resultFull.menu.button) - { - if (rootButton.name == null) - { - continue;//没有设置一级菜单 - } - var availableSubButton = rootButton.sub_button.Count(z => !string.IsNullOrEmpty(z.name));//可用二级菜单按钮数量 - if (availableSubButton == 0) - { - //底部单击按钮 - if (rootButton.type == null || - (rootButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase) - && string.IsNullOrEmpty(rootButton.key))) - { - throw new WeixinException("单击按钮的key不能为空!"); - } - - if (rootButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase)) - { - //点击 - bg.button.Add(new ClickMenuButton() - { - name = rootButton.name, - key = rootButton.key, - type = rootButton.type - }); - } - else - { - //URL - bg.button.Add(new ViewMenuButton() - { - name = rootButton.name, - url = rootButton.url, - type = rootButton.type - }); - } - } - else if (availableSubButton < 2) - { - throw new WeixinException("子菜单至少需要填写2个!"); - } - else - { - //底部二级菜单 - var subButton = new SubMenuButton(rootButton.name); - bg.button.Add(subButton); - - foreach (var subSubButton in rootButton.sub_button) - { - if (subSubButton.name == null) - { - continue; //没有设置菜单 - } - - if (subSubButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase) - && string.IsNullOrEmpty(subSubButton.key)) - { - throw new WeixinException("单击按钮的key不能为空!"); - } - - - if (subSubButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase)) - { - //点击 - subButton.sub_button.Add(new ClickMenuButton() - { - name = subSubButton.name, - key = subSubButton.key, - type = subSubButton.type - }); - } - else - { - //URL - subButton.sub_button.Add(new ViewMenuButton() - { - name = subSubButton.name, - url = subSubButton.url, - type = subSubButton.type - }); - } - } - } - } - - if (bg.button.Count < 2) - { - throw new WeixinException("一级菜单按钮至少为2个!"); - } - - result = new GetMenuResult() - { - menu = bg - }; - } - catch (Exception ex) - { - throw new WeixinException(ex.Message, ex); - } - return result; - } - - #endregion - - /// - /// 删除菜单 - /// - /// - /// - public static async Task DeleteMenu(string accessToken) - { - var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}", accessToken); - var response = await new HttpClient().GetAsync(url); - response.EnsureSuccessStatusCode(); - var json = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(json); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using Myvas.AspNetCore.Weixin; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Myvas.AspNetCore.Weixin +{ + public static class MenuApi + { + ///// + ///// 特殊符号转义 + ///// + ///// + ///// + //private static string ButtonNameEncode(string name) + //{ + // //直接用UrlEncode不行,显示内容超长 + // return name.Replace("&", "%26"); + //} + + /// + /// 创建菜单 + /// + /// + /// 菜单内容 + /// + public static async Task CreateMenu(string accessToken, ButtonGroup buttonData) + { + var api = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}"; + api = string.Format(api, accessToken); + + + ////对特殊符号进行URL转义 + //foreach (var button in buttonData.button) + //{ + // button.name = ButtonNameEncode(button.name);//button.name.UrlEncode(); + // if (button is SubButton) + // { + // var subButtonList = button as SubButton; + // foreach (var subButton in subButtonList.sub_button) + // { + // subButton.name = ButtonNameEncode(button.name);//button.name.UrlEncode(); + // } + // } + //} + + var tokenRequestParameters = new Dictionary() + { + //{ "client_id", Options.ClientId }, + //{ "redirect_uri", redirectUri }, + //{ "client_secret", Options.ClientSecret }, + //{ "code", code }, + //{ "grant_type", "authorization_code" }, + }; + var requestContent = new FormUrlEncodedContent(tokenRequestParameters); + + var requestMessage = new HttpRequestMessage(HttpMethod.Post, api); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Content = requestContent; + + var response = await new HttpClient().SendAsync(requestMessage); + response.EnsureSuccessStatusCode(); + + var json = await response.Content.ReadAsStringAsync(); + return json.FromJson(); + } + + /// + /// 创建菜单 + /// + /// + /// 菜单内容 + /// + public static async Task CreateMenuAsync(string accessToken, string menuJson) + { + var api = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}"; + api = string.Format(api, accessToken); + + var requestContent = new StringContent(menuJson); + + var requestMessage = new HttpRequestMessage(HttpMethod.Post, api); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Content = requestContent; + + var response = await new HttpClient().SendAsync(requestMessage); + response.EnsureSuccessStatusCode(); + + var json = await response.Content.ReadAsStringAsync(); + return json.FromJson(); + } + + + #region GetMenu + /// + /// 获取单击按钮 + /// + /// + /// + [Obsolete("配合GetMenuFromJson方法使用")] + private static ClickMenuButton GetSingleButtonFromJsonObject(Dictionary objs) + { + var sb = new ClickMenuButton() + { + key = objs["key"] as string, + name = objs["name"] as string, + type = objs["UploadMediaType"] as string + }; + return sb; + } + + + /// + /// 从JSON字符串获取菜单对象 + /// + /// + /// + [Obsolete("此方法通过判断GetMenuResult并结合object类型转换得到结果。结果准确。但更推荐使用GetMenuFromJsonResult方法。")] + public static GetMenuResult GetMenuFromJson(string json) + { + var finalResult = new GetMenuResult(); + + try + { + //@"{""menu"":{""button"":[{""UploadMediaType"":""click"",""name"":""单击测试"",""key"":""OneClick"",""sub_button"":[]},{""name"":""二级菜单"",""sub_button"":[{""UploadMediaType"":""click"",""name"":""返回文本"",""key"":""SubClickRoot_Text"",""sub_button"":[]},{""UploadMediaType"":""click"",""name"":""返回图文"",""key"":""SubClickRoot_News"",""sub_button"":[]},{""UploadMediaType"":""click"",""name"":""返回音乐"",""key"":""SubClickRoot_Music"",""sub_button"":[]}]}]}}" + object jsonResult = null; + + jsonResult = JsonSerializer.Deserialize(json); + + var fullResult = jsonResult as Dictionary; + if (fullResult != null && fullResult.ContainsKey("menu")) + { + //得到菜单 + var menu = fullResult["menu"]; + var buttons = (menu as Dictionary)["button"] as object[]; + + foreach (var rootButton in buttons) + { + var fullButton = rootButton as Dictionary; + if (fullButton.ContainsKey("key") && !string.IsNullOrEmpty(fullButton["key"] as string)) + { + //按钮,无下级菜单 + finalResult.menu.button.Add(GetSingleButtonFromJsonObject(fullButton)); + } + else + { + //二级菜单 + var subButton = new SubMenuButton(fullButton["name"] as string); + finalResult.menu.button.Add(subButton); + foreach (var sb in fullButton["sub_button"] as object[]) + { + subButton.sub_button.Add(GetSingleButtonFromJsonObject(sb as Dictionary)); + } + } + } + } + else if (fullResult != null && fullResult.ContainsKey("errmsg")) + { + //菜单不存在 + throw new WeixinException(fullResult["errmsg"] as string, null, null); + } + } + catch (WeixinException) + { + finalResult = null; + + //如果没有惨淡会返回错误代码:46003:menu no exist + } + catch (Exception) + { + //其他异常 + finalResult = null; + } + return finalResult; + } + + + /// + /// 获取当前菜单,如果菜单不存在,将返回null + /// + /// + /// + public static async Task GetMenuAsync(string accessToken) + { + var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", accessToken); + + var responseMessage = await new HttpClient().GetAsync(url); + responseMessage.EnsureSuccessStatusCode(); + var s = await responseMessage.Content.ReadAsStringAsync(); + var json = JsonDocument.Parse(s); + return json; + } + + public static GetMenuResult Parse(string json) + { + GetMenuResult finalResult; + try + { + var jsonResult = JsonSerializer.Deserialize(json); + if (jsonResult.menu == null || jsonResult.menu.button.Count == 0) + { + throw new WeixinException(jsonResult.errmsg); + } + + finalResult = GetMenuFromJsonResult(jsonResult); + } + catch (WeixinException) + { + finalResult = null; + } + + return finalResult; + } + + /// + /// 根据微信返回的Json数据得到可用的GetMenuResult结果 + /// + /// + /// + public static GetMenuResult GetMenuFromJsonResult(GetMenuResultFull resultFull) + { + GetMenuResult result = null; + try + { + //重新整理按钮信息 + ButtonGroup bg = new ButtonGroup(); + foreach (var rootButton in resultFull.menu.button) + { + if (rootButton.name == null) + { + continue;//没有设置一级菜单 + } + var availableSubButton = rootButton.sub_button.Count(z => !string.IsNullOrEmpty(z.name));//可用二级菜单按钮数量 + if (availableSubButton == 0) + { + //底部单击按钮 + if (rootButton.type == null || + (rootButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase) + && string.IsNullOrEmpty(rootButton.key))) + { + throw new WeixinException("单击按钮的key不能为空!"); + } + + if (rootButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase)) + { + //点击 + bg.button.Add(new ClickMenuButton() + { + name = rootButton.name, + key = rootButton.key, + type = rootButton.type + }); + } + else + { + //URL + bg.button.Add(new ViewMenuButton() + { + name = rootButton.name, + url = rootButton.url, + type = rootButton.type + }); + } + } + else if (availableSubButton < 2) + { + throw new WeixinException("子菜单至少需要填写2个!"); + } + else + { + //底部二级菜单 + var subButton = new SubMenuButton(rootButton.name); + bg.button.Add(subButton); + + foreach (var subSubButton in rootButton.sub_button) + { + if (subSubButton.name == null) + { + continue; //没有设置菜单 + } + + if (subSubButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase) + && string.IsNullOrEmpty(subSubButton.key)) + { + throw new WeixinException("单击按钮的key不能为空!"); + } + + + if (subSubButton.type.Equals("CLICK", StringComparison.OrdinalIgnoreCase)) + { + //点击 + subButton.sub_button.Add(new ClickMenuButton() + { + name = subSubButton.name, + key = subSubButton.key, + type = subSubButton.type + }); + } + else + { + //URL + subButton.sub_button.Add(new ViewMenuButton() + { + name = subSubButton.name, + url = subSubButton.url, + type = subSubButton.type + }); + } + } + } + } + + if (bg.button.Count < 2) + { + throw new WeixinException("一级菜单按钮至少为2个!"); + } + + result = new GetMenuResult() + { + menu = bg + }; + } + catch (Exception ex) + { + throw new WeixinException(ex.Message, ex); + } + return result; + } + + #endregion + + /// + /// 删除菜单 + /// + /// + /// + public static async Task DeleteMenu(string accessToken) + { + var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}", accessToken); + var response = await new HttpClient().GetAsync(url); + response.EnsureSuccessStatusCode(); + var json = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(json); + } + } +} diff --git a/src/Weixin/Basic/Events/Context/WeixinReceivedContext.cs b/src/Weixin/Basic/Events/Context/WeixinReceivedContext.cs index a40d263..585196d 100644 --- a/src/Weixin/Basic/Events/Context/WeixinReceivedContext.cs +++ b/src/Weixin/Basic/Events/Context/WeixinReceivedContext.cs @@ -1,41 +1,50 @@ -namespace Myvas.AspNetCore.Weixin -{ - /// - /// Contains information about the login session as well as the user . - /// - public class WeixinReceivedContext //: BaseWeixinContext - where TEventArgs : ReceivedEventArgs - { - /// - /// Initializes a - /// - /// The HTTP environment - /// The options for Weixin - /// The sender respective for - /// - public WeixinReceivedContext( - WeixinMessageHandler sender, - TEventArgs args, - bool needEncrypt) - { - Sender = sender; - Args = args; - NeedEncrypt = needEncrypt; - } - - /// - /// Gets the sender - /// - public WeixinMessageHandler Sender { get; } - - /// - /// Gets the event args - /// - public TEventArgs Args { get; } - - /// - /// Gets whether WeixinMessageEncryptor is must. - /// - public bool NeedEncrypt { get; } - } -} +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Authentication; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Security.Claims; +using System.Text; + +namespace Myvas.AspNetCore.Weixin +{ + /// + /// Contains information about the login session as well as the user . + /// + public class WeixinReceivedContext //: BaseWeixinContext + where TEventArgs : ReceivedEventArgs + { + /// + /// Initializes a + /// + /// The HTTP environment + /// The options for Weixin + /// The sender respective for + /// + public WeixinReceivedContext( + WeixinMessageHandler sender, + TEventArgs args, + bool needEncrypt) + { + Sender = sender; + Args = args; + NeedEncrypt = needEncrypt; + } + + /// + /// Gets the sender + /// + public WeixinMessageHandler Sender { get; } + + /// + /// Gets the event args + /// + public TEventArgs Args { get; } + + /// + /// Gets whether WeixinMessageEncryptor is must. + /// + public bool NeedEncrypt { get; } + } +} diff --git a/src/Weixin/Builder/WeixinOptions.cs b/src/Weixin/Builder/WeixinOptions.cs index 3f458db..c5674e2 100644 --- a/src/Weixin/Builder/WeixinOptions.cs +++ b/src/Weixin/Builder/WeixinOptions.cs @@ -1,8 +1,11 @@ -namespace Myvas.AspNetCore.Weixin -{ - public class WeixinOptions - { - public string AppId { get; set; } - public string AppSecret { get; set; } - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace Myvas.AspNetCore.Weixin +{ + public class WeixinOptions + { + public string AppId { get; set; } + public string AppSecret { get; set; } + } +} diff --git a/src/Weixin/Extras/Extensions/MessageBase.Xml.cs b/src/Weixin/Extras/Extensions/MessageBase.Xml.cs index d3beb03..2265c76 100644 --- a/src/Weixin/Extras/Extensions/MessageBase.Xml.cs +++ b/src/Weixin/Extras/Extensions/MessageBase.Xml.cs @@ -1,257 +1,256 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Xml.Linq; - -namespace Myvas.AspNetCore.Weixin -{ - - internal static class Extensions - { - /// - /// 根据XML信息填充实实体 - /// - /// MessageBase为基类的类型,Response和Request都可以 - /// 实体 - /// XML - public static void FillEntityWithXml(this T entity, XDocument doc) where T : /*MessageBase*/ class, new() - { - entity = entity ?? new T(); - var root = doc.Root; - - var props = entity.GetType().GetProperties(); - foreach (var prop in props) - { - var propName = prop.Name; - if (root.Element(propName) != null) - { - if (prop.PropertyType == typeof(DateTime)) - { - prop.SetValue(entity, WeixinTimestampHelper.ToLocalTime(root.Element(propName).Value), null); - } - else if (prop.PropertyType == typeof(Boolean) && (propName == "FuncFlag")) - { - prop.SetValue(entity, root.Element(propName).Value == "1", null); - } - else if (prop.PropertyType == typeof(Int32)) - { - prop.SetValue(entity, int.Parse(root.Element(propName).Value), null); - } - else if (prop.PropertyType == typeof(Int64)) - { - - prop.SetValue(entity, long.Parse(root.Element(propName).Value), null); - } - else if (prop.PropertyType == typeof(Decimal)) - { - prop.SetValue(entity, decimal.Parse(root.Element(propName).Value), null); - } - else if (prop.PropertyType == typeof(Double)) - { - prop.SetValue(entity, double.Parse(root.Element(propName).Value), null); - } - else if (prop.PropertyType == typeof(ReceivedMsgType)) - { - //已设为只读 - //prop.SetValue(entity, MsgTypeHelper.GetRequestMsgType(root.Element(propName).Value), null); - } - else if (prop.PropertyType == typeof(ResponseMsgType)) - { - //已设为只读 - //prop.SetValue(entity, MsgTypeHelper.GetResponseMsgType(root.Element(propName).Value), null); - } - else if (prop.PropertyType == typeof(ReceivedEventType)) - { - //已设为只读 - //prop.SetValue(entity, EventHelper.GetEventType(root.Element(propName).Value), null); - } - else if (prop.PropertyType == typeof(List
)) - { - //var genericArguments = prop.PropertyType.GetGenericArguments(); - //if (genericArguments[0].Name == "Article")//ResponseMessageNews适用 - { - //文章下属节点item - List
articles = new List
(); - foreach (var item in root.Element(propName).Elements("item")) - { - var article = new Article(); - FillEntityWithXml(article, new XDocument(item)); - articles.Add(article); - } - prop.SetValue(entity, articles, null); - } - } - else if (prop.PropertyType == typeof(Music)) - { - Music music = new Music(); - FillEntityWithXml(music, new XDocument(root.Element(propName))); - prop.SetValue(entity, music, null); - } - else if (prop.PropertyType == typeof(Image)) - { - Image image = new Image(); - FillEntityWithXml(image, new XDocument(root.Element(propName))); - prop.SetValue(entity, image, null); - } - else if (prop.PropertyType == typeof(Voice)) - { - Voice voice = new Voice(); - FillEntityWithXml(voice, new XDocument(root.Element(propName))); - prop.SetValue(entity, voice, null); - } - else if (prop.PropertyType == typeof(Video)) - { - Video video = new Video(); - FillEntityWithXml(video, new XDocument(root.Element(propName))); - prop.SetValue(entity, video, null); - } - else - { - prop.SetValue(entity, root.Element(propName).Value, null); - } - } - } - } - - /// - /// 将实体转为XML - /// - /// RequestMessage或ResponseMessage - /// 实体 - /// - public static XDocument ConvertEntityToXml(this T entity) where T : class , new() - { - entity = entity ?? new T(); - var doc = new XDocument(); - doc.Add(new XElement("xml")); - var root = doc.Root; - - /* 注意! - * 经过测试,微信对字段排序有严格要求,这里对排序进行强制约束 - */ - var propNameOrder = new List() { "ToUserName", "FromUserName", "CreateTime", "MsgType" }; - //不同返回类型需要对应不同特殊格式的排序 - if (entity is ResponseMessageNews) - { - propNameOrder.AddRange(new[] { "ArticleCount", "Articles", "FuncFlag",/*以下是Atricle属性*/ "Title ", "Description ", "PicUrl", "Url" }); - } - else if (entity is ResponseMessageMusic) - { - propNameOrder.AddRange(new[] { "Music", "FuncFlag", "ThumbMediaId",/*以下是Music属性*/ "Title ", "Description ", "MusicUrl", "HQMusicUrl" }); - } - else if (entity is ResponseMessageImage) - { - propNameOrder.AddRange(new[] { "Image",/*以下是Image属性*/ "MediaId " }); - } - else if (entity is ResponseMessageVoice) - { - propNameOrder.AddRange(new[] { "Voice",/*以下是Voice属性*/ "MediaId " }); - } - else if (entity is ResponseMessageVideo) - { - propNameOrder.AddRange(new[] { "Video",/*以下是Video属性*/ "MediaId ", "Title", "Description" }); - } - else - { - //如Text类型 - propNameOrder.AddRange(new[] { "Content", "FuncFlag" }); - } - - Func orderByPropName = propNameOrder.IndexOf; - - var props = entity.GetType().GetProperties().OrderBy(p => orderByPropName(p.Name)).ToList(); - foreach (var prop in props) - { - var propName = prop.Name; - if (propName == "Articles") - { - //文章列表 - var atriclesElement = new XElement("Articles"); - var articales = prop.GetValue(entity, null) as List
; - foreach (var articale in articales) - { - var subNodes = ConvertEntityToXml(articale).Root.Elements(); - atriclesElement.Add(new XElement("item", subNodes)); - } - root.Add(atriclesElement); - } - else if (propName == "Music" || propName == "Image" || propName == "Video" || propName == "Voice") - { - //音乐、图片、视频、语音格式 - var musicElement = new XElement(propName); - var media = prop.GetValue(entity, null);// as Music; - var subNodes = ConvertEntityToXml(media).Root.Elements(); - musicElement.Add(subNodes); - root.Add(musicElement); - } - else - { - switch (prop.PropertyType.Name) - { - case "String": - root.Add(new XElement(propName, - new XCData(prop.GetValue(entity, null) as string ?? ""))); - break; - case "DateTime": - root.Add(new XElement(propName, WeixinTimestampHelper.FromBeijingTime((DateTime)prop.GetValue(entity, null)))); - break; - case "Boolean": - if (propName == "FuncFlag") - { - root.Add(new XElement(propName, (bool)prop.GetValue(entity, null) ? "1" : "0")); - } - else - { - goto default; - } - break; - case "ResponseMsgType": - root.Add(new XElement(propName, new XCData(prop.GetValue(entity, null).ToString().ToLower()))); - break; - case "Article": - root.Add(new XElement(propName, prop.GetValue(entity, null).ToString().ToLower())); - break; - default: - root.Add(new XElement(propName, prop.GetValue(entity, null))); - break; - } - } - } - return doc; - } - - /// - /// 将实体转为XML字符串 - /// - /// RequestMessage或ResponseMessage - /// 实体 - /// - public static string ConvertEntityToXmlString(this T entity) where T : class , new() - { - return entity.ConvertEntityToXml().ToString(); - } - - /// - /// ResponseMessageBase.CreateFromRequestMessage(requestMessage)的扩展方法 - /// - /// 需要生成的ResponseMessage类型 - /// IRequestMessageBase接口下的接收信息类型 - /// - public static T CreateResponseMessage(this IRequestMessageBase requestMessage) where T : ResponseMessageBase - { - return ResponseMessageBase.CreateFromRequestMessage(requestMessage); - } - - /// - /// ResponseMessageBase.CreateFromResponseXml(xml)的扩展方法 - /// - /// 返回给服务器的Response Xml - /// - public static IResponseMessageBase CreateResponseMessage(this string xml) - { - return ResponseMessageBase.CreateFromResponseXml(xml); - } - } - -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; + +namespace Myvas.AspNetCore.Weixin +{ + internal static class Extensions + { + /// + /// 根据XML信息填充实实体 + /// + /// MessageBase为基类的类型,Response和Request都可以 + /// 实体 + /// XML + public static void FillEntityWithXml(this T entity, XDocument doc) where T : /*MessageBase*/ class, new() + { + entity = entity ?? new T(); + var root = doc.Root; + + var props = entity.GetType().GetProperties(); + foreach (var prop in props) + { + var propName = prop.Name; + if (root.Element(propName) != null) + { + if (prop.PropertyType == typeof(DateTime)) + { + prop.SetValue(entity, WeixinTimestampHelper.ToLocalTime(root.Element(propName).Value), null); + } + else if (prop.PropertyType == typeof(Boolean) && (propName == "FuncFlag")) + { + prop.SetValue(entity, root.Element(propName).Value == "1", null); + } + else if (prop.PropertyType == typeof(Int32)) + { + prop.SetValue(entity, int.Parse(root.Element(propName).Value), null); + } + else if (prop.PropertyType == typeof(Int64)) + { + + prop.SetValue(entity, long.Parse(root.Element(propName).Value), null); + } + else if (prop.PropertyType == typeof(Decimal)) + { + prop.SetValue(entity, decimal.Parse(root.Element(propName).Value), null); + } + else if (prop.PropertyType == typeof(Double)) + { + prop.SetValue(entity, double.Parse(root.Element(propName).Value), null); + } + else if (prop.PropertyType == typeof(ReceivedMsgType)) + { + //已设为只读 + //prop.SetValue(entity, MsgTypeHelper.GetRequestMsgType(root.Element(propName).Value), null); + } + else if (prop.PropertyType == typeof(ResponseMsgType)) + { + //已设为只读 + //prop.SetValue(entity, MsgTypeHelper.GetResponseMsgType(root.Element(propName).Value), null); + } + else if (prop.PropertyType == typeof(ReceivedEventType)) + { + //已设为只读 + //prop.SetValue(entity, EventHelper.GetEventType(root.Element(propName).Value), null); + } + else if (prop.PropertyType == typeof(List
)) + { + //var genericArguments = prop.PropertyType.GetGenericArguments(); + //if (genericArguments[0].Name == "Article")//ResponseMessageNews适用 + { + //文章下属节点item + List
articles = new List
(); + foreach (var item in root.Element(propName).Elements("item")) + { + var article = new Article(); + FillEntityWithXml(article, new XDocument(item)); + articles.Add(article); + } + prop.SetValue(entity, articles, null); + } + } + else if (prop.PropertyType == typeof(Music)) + { + Music music = new Music(); + FillEntityWithXml(music, new XDocument(root.Element(propName))); + prop.SetValue(entity, music, null); + } + else if (prop.PropertyType == typeof(Image)) + { + Image image = new Image(); + FillEntityWithXml(image, new XDocument(root.Element(propName))); + prop.SetValue(entity, image, null); + } + else if (prop.PropertyType == typeof(Voice)) + { + Voice voice = new Voice(); + FillEntityWithXml(voice, new XDocument(root.Element(propName))); + prop.SetValue(entity, voice, null); + } + else if (prop.PropertyType == typeof(Video)) + { + Video video = new Video(); + FillEntityWithXml(video, new XDocument(root.Element(propName))); + prop.SetValue(entity, video, null); + } + else + { + prop.SetValue(entity, root.Element(propName).Value, null); + } + } + } + } + + /// + /// 将实体转为XML + /// + /// RequestMessage或ResponseMessage + /// 实体 + /// + public static XDocument ConvertEntityToXml(this T entity) where T : class , new() + { + entity = entity ?? new T(); + var doc = new XDocument(); + doc.Add(new XElement("xml")); + var root = doc.Root; + + /* 注意! + * 经过测试,微信对字段排序有严格要求,这里对排序进行强制约束 + */ + var propNameOrder = new List() { "ToUserName", "FromUserName", "CreateTime", "MsgType" }; + //不同返回类型需要对应不同特殊格式的排序 + if (entity is ResponseMessageNews) + { + propNameOrder.AddRange(new[] { "ArticleCount", "Articles", "FuncFlag",/*以下是Atricle属性*/ "Title ", "Description ", "PicUrl", "Url" }); + } + else if (entity is ResponseMessageMusic) + { + propNameOrder.AddRange(new[] { "Music", "FuncFlag", "ThumbMediaId",/*以下是Music属性*/ "Title ", "Description ", "MusicUrl", "HQMusicUrl" }); + } + else if (entity is ResponseMessageImage) + { + propNameOrder.AddRange(new[] { "Image",/*以下是Image属性*/ "MediaId " }); + } + else if (entity is ResponseMessageVoice) + { + propNameOrder.AddRange(new[] { "Voice",/*以下是Voice属性*/ "MediaId " }); + } + else if (entity is ResponseMessageVideo) + { + propNameOrder.AddRange(new[] { "Video",/*以下是Video属性*/ "MediaId ", "Title", "Description" }); + } + else + { + //如Text类型 + propNameOrder.AddRange(new[] { "Content", "FuncFlag" }); + } + + Func orderByPropName = propNameOrder.IndexOf; + + var props = entity.GetType().GetProperties().OrderBy(p => orderByPropName(p.Name)).ToList(); + foreach (var prop in props) + { + var propName = prop.Name; + if (propName == "Articles") + { + //文章列表 + var atriclesElement = new XElement("Articles"); + var articales = prop.GetValue(entity, null) as List
; + foreach (var articale in articales) + { + var subNodes = ConvertEntityToXml(articale).Root.Elements(); + atriclesElement.Add(new XElement("item", subNodes)); + } + root.Add(atriclesElement); + } + else if (propName == "Music" || propName == "Image" || propName == "Video" || propName == "Voice") + { + //音乐、图片、视频、语音格式 + var musicElement = new XElement(propName); + var media = prop.GetValue(entity, null);// as Music; + var subNodes = ConvertEntityToXml(media).Root.Elements(); + musicElement.Add(subNodes); + root.Add(musicElement); + } + else + { + switch (prop.PropertyType.Name) + { + case "String": + root.Add(new XElement(propName, + new XCData(prop.GetValue(entity, null) as string ?? ""))); + break; + case "DateTime": + root.Add(new XElement(propName, WeixinTimestampHelper.FromBeijingTime((DateTime)prop.GetValue(entity, null)))); + break; + case "Boolean": + if (propName == "FuncFlag") + { + root.Add(new XElement(propName, (bool)prop.GetValue(entity, null) ? "1" : "0")); + } + else + { + goto default; + } + break; + case "ResponseMsgType": + root.Add(new XElement(propName, new XCData(prop.GetValue(entity, null).ToString().ToLower()))); + break; + case "Article": + root.Add(new XElement(propName, prop.GetValue(entity, null).ToString().ToLower())); + break; + default: + root.Add(new XElement(propName, prop.GetValue(entity, null))); + break; + } + } + } + return doc; + } + + /// + /// 将实体转为XML字符串 + /// + /// RequestMessage或ResponseMessage + /// 实体 + /// + public static string ConvertEntityToXmlString(this T entity) where T : class , new() + { + return entity.ConvertEntityToXml().ToString(); + } + + /// + /// ResponseMessageBase.CreateFromRequestMessage(requestMessage)的扩展方法 + /// + /// 需要生成的ResponseMessage类型 + /// IRequestMessageBase接口下的接收信息类型 + /// + public static T CreateResponseMessage(this IRequestMessageBase requestMessage) where T : ResponseMessageBase + { + return ResponseMessageBase.CreateFromRequestMessage(requestMessage); + } + + /// + /// ResponseMessageBase.CreateFromResponseXml(xml)的扩展方法 + /// + /// 返回给服务器的Response Xml + /// + public static IResponseMessageBase CreateResponseMessage(this string xml) + { + return ResponseMessageBase.CreateFromResponseXml(xml); + } + } + +} diff --git a/src/Weixin/Myvas.AspNetCore.Weixin.csproj b/src/Weixin/Myvas.AspNetCore.Weixin.csproj index cd05e49..01b79ba 100644 --- a/src/Weixin/Myvas.AspNetCore.Weixin.csproj +++ b/src/Weixin/Myvas.AspNetCore.Weixin.csproj @@ -1,19 +1,19 @@ - - - - netcoreapp3.1 - Myvas.AspNetCore.Weixin - 3.1.100.5 - MIT - Myvas.AspNetCore.Weixin - Myvas - An AspNetCore middleware for Tencent Weixin(aka. Wechat) message handler and apis. - https://github.com/myvas/AspNetCore.Weixin - myvas;AspNetCore;Weixin - https://github.com/myvas/AspNetCore.Weixin - - - - - - + + + + netcoreapp3.1 + Myvas.AspNetCore.Weixin + 3.1.100.5 + MIT + Myvas.AspNetCore.Weixin + Myvas + An AspNetCore middleware for Tencent Weixin(aka. Wechat) message handler and apis. + https://github.com/myvas/AspNetCore.Weixin + myvas;AspNetCore;Weixin + https://github.com/myvas/AspNetCore.Weixin + + + + + + \ No newline at end of file diff --git a/src/Weixin/WelcomePage/MessageProtection/WeixinMessageEncodingTypes.cs b/src/Weixin/WelcomePage/MessageProtection/WeixinMessageEncodingTypes.cs index c43187f..0c03b7c 100644 --- a/src/Weixin/WelcomePage/MessageProtection/WeixinMessageEncodingTypes.cs +++ b/src/Weixin/WelcomePage/MessageProtection/WeixinMessageEncodingTypes.cs @@ -1,9 +1,11 @@ -namespace Myvas.AspNetCore.Weixin -{ - public static class WeixinMessageEncodingTypes - { - public const string Compatible = "Compatible"; - public const string Clear = "Clear"; - public const string Encrypted = "Encrypted"; - } -} +using Microsoft.AspNetCore.Http; + +namespace Myvas.AspNetCore.Weixin +{ + public static class WeixinMessageEncodingTypes + { + public const string Compatible = "Compatible"; + public const string Clear = "Clear"; + public const string Encrypted = "Encrypted"; + } +}