本来想写这个帖子已经很久了,但是公司事情多,做着做着就忘记了。公司因为需要做接口,而且用的还是asp.net的老框架,使用Handler来做,没得办法,自己照着MVC写了一个通过的接口操作模板。

上送json数据,返回的也是json数据。可以像MVC一样自动绑定并可以进行DataAnnotations验证。尽量达到在业务逻辑处理区域不用对上送参数做过多的获取和判断,能一次搞定就一次搞定。

话不多说,上代码!!!

BaseClass:用作接口参数的基类。接口参数类型可以继承该类,也可以不继承,或自己定义其他基类。

public abstract class BaseClass { }

EmptyClass:一个空类。当做一个参数类型传入到接口中,占用地方而已。

public class EmptyClass : BaseClass { }

ModelError:错误消息载体类型。

    [Serializable]
public class ModelError {
public ModelError(Exception exception) : this(exception, null) { } public ModelError(string errorMessage) {
this.ErrorMessage = errorMessage ?? string.Empty;
} public ModelError(Exception exception, string errorMessage) : this(errorMessage) {
if (exception == null)
throw new ArgumentNullException("exception");
this.Exception = exception;
} public Exception Exception { get; private set; }
public string ErrorMessage { get; private set; }
}

ModelErrorCollection:错误消息载体集合。

    [Serializable]
public class ModelErrorCollection : Collection<ModelError> {
public void Add(string errorMessage) {
base.Add(new ModelError(errorMessage));
} public void Add(Exception exception) {
base.Add(new ModelError(exception));
}
}

ModelState:模型绑定状态。

    /// <summary>
/// 模型绑定状态
/// </summary>
[Serializable]
public class ModelState {
private ModelErrorCollection _errors = new ModelErrorCollection();
public bool IsValid {
get {
return _errors.Count == ;
}
}
public ModelErrorCollection Errors {
get {
return _errors;
}
}
}

ModelBinder:模型绑定抽象类,需要继承此抽象类。用来绑定上送参数并验证的基类。

    /// <summary>
/// 模型绑定抽象类,需要继承此抽象类。
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class ModelBinder<T> where T : class {
protected ModelState _modelState; /// <summary>
/// 模型绑定状态
/// </summary>
public ModelState ModelState {
get {
if (_modelState == null)
_modelState = new ModelState();
return _modelState;
}
} /// <summary>
/// 绑定操作
/// </summary>
/// <returns></returns>
public abstract T Binder(); /// <summary>
/// 验证实体数据合法性。如果有错误,请在ModelState参数中获取。
/// </summary>
/// <param name="entity"></param>
protected void Valide(object entity) {
if (entity == null)
return;
//获取T类型的所有公共属性
Type type = entity.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); if (properties != null && properties.Count() > ) {
//针对每一个公共属性,获取其特性
foreach (var property in properties) {
//如果当前属性为一个自定义类型
if (property.PropertyType != typeof(object) && Type.GetTypeCode(property.PropertyType) == TypeCode.Object) {
this.Valide(property.GetValue(entity, null));
}
else
ValideProperty(entity, property); if (!_modelState.IsValid)
break;
}
}
} /// <summary>
/// 验证属性的每一个特性约束
/// </summary>
/// <param name="entity"></param>
/// <param name="property"></param>
private void ValideProperty(object entity, PropertyInfo property) {
if (entity != null && property != null) {
var attributes = property.GetCustomAttributes(typeof(ValidationAttribute), false);
foreach (ValidationAttribute attribute in attributes)
ValidatePropertyAttribute(entity, property, attribute);
}
} /// <summary>
/// 使用特性对属性进行验证
/// </summary>
/// <param name="entity"></param>
/// <param name="property"></param>
/// <param name="attribute"></param>
private void ValidatePropertyAttribute(object entity, PropertyInfo property, ValidationAttribute attribute) {
if (entity != null && property != null && attribute != null) {
//找到该属性
//注明:每一个函数都应当具有独立性.
PropertyInfo currentProperty = entity.GetType().GetProperties().Where(p => p.Name == property.Name).FirstOrDefault(); //判断当前特性是否有IsRequiredInstance字段,这是自定义的特性,用于验证同一个实例中两个不同共有属性的值
PropertyInfo[] pros = attribute.GetType().GetProperties();
if (pros.Where(it => it.Name == "IsRequiredInstance" && it.PropertyType == typeof(bool)).FirstOrDefault() != null)
attribute.GetType().GetProperty("Instance").SetValue(attribute, entity, null); if (currentProperty != null) {
var value = currentProperty.GetValue(entity, null);
if (!attribute.IsValid(value))
_modelState.Errors.Add(attribute.ErrorMessage);
}
}
} }

BaseHandler:一般处理逻辑的基类。

    /// <summary>
/// 一般处理逻辑的基类。
/// </summary>
/// <typeparam name="T">请求参数模型</typeparam>
/// <typeparam name="R">返回参数模型</typeparam>
public abstract class BaseHandler<T, R> : ModelBinder<T> where T : class where R : class {
protected readonly ILog log;
public BaseHandler() {
log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}
public BaseHandler(string typeName) {
if ((typeName ?? "").Trim() != "")
log = LogManager.GetLogger(typeName);
else
log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
} /// <summary>
/// 处理接口API消息
/// </summary>
/// <returns></returns>
public abstract R Process(); /// <summary>
/// 真正需要处理的接口逻辑
/// </summary>
/// <param name="param">客户端传过来的请求参数</param>
/// <returns></returns>
protected abstract R DoWork(T param);
}

IndexHandler:一般业务接口的模板方法处理器。该接口是基础性接口,如果有其他业务需求,请自行另外自定义或继承该接口。

    /// <summary>
/// 一般业务接口的模板方法处理器。
/// 该接口是基础性接口,如果有其他业务需求,请自行另外自定义或继承该接口。
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class IndexHandler<T> : BaseHandler<T, BaseResponseResult> where T : class {
public IndexHandler() : base() { }
public IndexHandler(string typeName) : base(typeName) { } /// <summary>
/// 对实体模型进行绑定和参数的特性验证
/// </summary>
/// <returns></returns>
public override T Binder() {
//初始化模型
T rc = default(T); try {
//初始化ModelState
if (_modelState == null)
_modelState = new ModelState();
else
_modelState.Errors.Clear(); //获取数据
Stream stream = HttpContext.Current.Request.InputStream;
stream.Seek(, SeekOrigin.Begin);
byte[] buffer = new byte[stream.Length];
int count = stream.Read(buffer, , buffer.Length);
if (count > ) {
string requestParam = Encoding.UTF8.GetString(buffer);
//绑定数据
rc = new JavaScriptSerializer().Deserialize<T>(requestParam);
if (rc != null) {
//验证数据合法性
base.Valide(rc);
}
else
_modelState.Errors.Add("绑定数据失败!");
}
else
_modelState.Errors.Add("请求参数为空!");
}
catch (Exception ex) {
_modelState.Errors.Add("绑定数据出现错误!");
} return rc;
} /// <summary>
/// 处理接口API消息
/// </summary>
/// <returns></returns>
public override BaseResponseResult Process() {
BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError); try {
//绑定请求参数
T requestParam = Binder();
//开启逻辑操作
if (_modelState.IsValid)
rc = DoWork(requestParam);
else {
StringBuilder sbuilder = new StringBuilder();
foreach (var error in _modelState.Errors)
sbuilder.Append(error.ErrorMessage); rc.SetResult(ErrorCode.InvalideParameter, sbuilder.ToString());
}
}
catch (Exception ex) {
rc.SetResult(ErrorCode.SystemError);
rc.returnData = null;
log.Error("Process", ex);
} return rc;
}
}

BaseResponseResult:返回结果。

    /// <summary>
/// 返回结果
/// </summary>
public class BaseResponseResult {
public BaseResponseResult() { } public BaseResponseResult(int returnValue, string returnMsg) {
_code = returnValue;
_message = returnMsg;
} public BaseResponseResult(ErrorCode code, string returnMsg="") {
SetResult(code, returnMsg);
} private int _code = ;
private string _message = "";
private string _contentType = "application/json";
/// <summary>
/// 错误码,0表示成功,其他表示失败
/// </summary>
public virtual int returnValue { get { return _code; } }
/// <summary>
/// 错误码,0表示成功,其他表示失败
/// </summary>
public virtual string returnMsg { get { return _message; } }
/// <summary>
/// 返回的数据,json格式
/// </summary>
public virtual object returnData { get; set; } /// <summary>
/// 设置返回状态码
/// </summary>
/// <param name="code"></param>
public virtual void SetCode(int code) {
_code = code;
} /// <summary>
/// 设置返回状态码
/// </summary>
/// <param name="code"></param>
public virtual void SetCode(ErrorCode code) {
SetResult(code);
} /// <summary>
/// 设置返回消息
/// </summary>
/// <param name="message"></param>
public virtual void SetMessage(string message) {
_message = message;
}
/// <summary>
/// 设置返回状态码和消息
/// </summary>
/// <param name="returnValue"></param>
/// <param name="returnMsg"></param>
public virtual void SetResult(int returnValue, string returnMsg) {
_code = returnValue;
_message = returnMsg;
} /// <summary>
///
/// </summary>
/// <param name="code">ErrorCode代码</param>
/// <param name="returnMsg">返回消息。如果此项不输入值,则自动设置默认值!</param>
public virtual void SetResult(ErrorCode code, string returnMsg="") {
this._code = (int)code;
this._message = (returnMsg??"").Trim()!=""?returnMsg:ErrorMsg.ErrorMessage[code];
}
/// <summary>
/// 设置返回消息体
/// </summary>
/// <param name="obj"></param>
public virtual void SetReturnData(params object[] obj) {
if (obj != null && obj.Length > ) {
this.returnData = obj.ToList();
}
} public virtual void SetReturnData(object obj) {
this.returnData = obj;
} public virtual void SetContentType(string contentType) {
this._contentType = contentType;
} /// <summary>
/// 将当前结果转化为JSON字符串
/// </summary>
/// <returns></returns>
public virtual string ToJson() {
return new JavaScriptSerializer().Serialize(this);
} public virtual void Response(bool isEnd = true) {
HttpContext.Current.Response.ContentType = _contentType;
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
HttpContext.Current.Response.Write(this.ToJson());
if (isEnd)
HttpContext.Current.Response.End();
}
}

ErrorCode:返回状态码枚举。

 public enum ErrorCode {
/// <summary>
/// 操作错误
/// </summary>
OperationError = -,
/// <summary>
/// 成功
/// </summary>
Success = ,
/// <summary>
/// 失败
/// </summary>
Failed = ,
/// <summary>
/// 无数据
/// </summary>
NoData=,
/// <summary>
/// 不存在此页面
/// </summary>
NotExistPage=,
/// <summary>
/// 无权限
/// </summary>
NoPermission=,
/// <summary>
/// 未登录
/// </summary>
NoLogin=,
/// <summary>
/// 被禁止
/// </summary>
Forbidden=,
/// <summary>
/// 请求参数格式不符合要求
/// </summary>
InvalideParameter = ,
/// <summary>
/// 无此接口
/// </summary>
NoAction = ,
/// <summary>
/// 系统错误
/// </summary>
SystemError = ,
} public class ErrorMsg {
public static Dictionary<ErrorCode, string> ErrorMessage = new Dictionary<ErrorCode, string> {
{ ErrorCode.OperationError,"服务器响应错误!"},
{ ErrorCode.Success,"成功!"},
{ ErrorCode.Failed, "失败!"},
{ ErrorCode.NoData, "查无数据!"},
{ ErrorCode.NotExistPage,"此页码不存在!"},
{ ErrorCode.InvalideParameter,"请求参数非法!"},
{ ErrorCode.NoAction,"无此接口!"},
{ ErrorCode.SystemError,"系统错误!"},
{ ErrorCode.NoPermission,"无权限操作此功能!" },
{ ErrorCode.NoLogin,"未登录!"},
{ ErrorCode.Forbidden,"操作被禁止!"},
};
}

接下来介绍使用方法:

IndexHandler_Base64:定义一个接收base64字符串参数的接口处理器。代码如下:

    /// <summary>
/// 一般业务接口的模板方法处理器,适用于参数为base64字符串的请求
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class IndexHandler_Base64<T> : IndexHandler<T> where T:class {
//当前接口是否需要登录后才能操作
protected bool _isLoginRequired = false; /// <summary>
/// 实例化一个只接收base64字符串的接口操作
/// </summary>
/// <param name="isLoginRequired">当前接口是否需要登录。true:需要 false:不需要(默认)</param>
public IndexHandler_Base64(bool isLoginRequired = false) : base() { _isLoginRequired = isLoginRequired; }
/// <summary>
/// 实例化一个只接收base64字符串的接口操作
/// </summary>
/// <param name="typeName">发起日志记录的类名</param>
/// <param name="isLoginRequired">当前接口是否需要登录。true:需要 false:不需要(默认)</param>
public IndexHandler_Base64(string typeName,bool isLoginRequired=false) : base(typeName) { _isLoginRequired = isLoginRequired; } /// <summary>
/// 对实体模型进行绑定和参数的特性验证
/// </summary>
/// <returns></returns>
public override T Binder() {
//初始化模型
T rc = default(T); try {
//初始化ModelState
if (_modelState == null)
_modelState = new ModelState();
else
_modelState.Errors.Clear(); //获取数据
Stream stream = HttpContext.Current.Request.InputStream;
stream.Seek(, SeekOrigin.Begin);
byte[] buffer = new byte[stream.Length];
int count = stream.Read(buffer, , buffer.Length);
if (count > ) {
string requestParam = Encoding.UTF8.GetString(buffer);
requestParam = Encoding.UTF8.GetString(Convert.FromBase64String(requestParam));
//绑定数据
rc = new JavaScriptSerializer().Deserialize<T>(requestParam);
if (rc != null) {
//验证数据合法性
base.Valide(rc);
}
else
_modelState.Errors.Add("绑定数据失败!");
}
else
_modelState.Errors.Add("请求参数为空!");
}
catch (Exception ex) {
_modelState.Errors.Add("绑定数据出现错误!");
} return rc;
} public override BaseResponseResult Process() {
//如果该接口需要登录后才能操作,则检查当前用户是否登录
if (_isLoginRequired) {
int userId = ;
try {
userId = Login.GetSession("UserID");
}
catch { }
if (userId <= )
return new BaseResponseResult(ErrorCode.NoLogin);
} return base.Process();
}
}

接下来定义一个Index.ashx文件,开始使用模板接口。

    /// <summary>
/// index 的摘要说明
/// </summary>
public class Index : IHttpHandler {
private readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
//当前登录的用户
public static int _thisUserId {
get {
return Login.GetSession("UserID");
}
}
public void ProcessRequest(HttpContext context) {
BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError, "获取数据失败!"); try {
string action = GetAction(); switch (action) {
//获取 xxx列表
case "":
rc = new ListHandler().Process();
break;
//获取 xxx信息
case "":
rc = new ItemHandler().Process();
break; default:
rc.SetResult(ErrorCode.NoAction);
break;
} }
catch (Exception ex) {
log.Error("ProcessRequest", ex);
rc.SetResult(ErrorCode.SystemError);
} rc.Response();
} public bool IsReusable {
get {
return false;
}
} /// <summary>
/// 获取Action接口名称
/// </summary>
/// <returns></returns>
private string GetAction() {
string rc = ""; try {
rc = HttpContext.Current.Request["action"] ?? "";
if ((rc ?? "").Trim() == "") {
Stream stream = HttpContext.Current.Request.InputStream;
if (stream != null) {
byte[] buffer = new byte[stream.Length];
int count = stream.Read(buffer, , buffer.Length);
if (count > ) {
try {
string requestParam = Encoding.UTF8.GetString(buffer);
requestParam = Encoding.UTF8.GetString(Convert.FromBase64String(requestParam));
JsonData jd = JsonMapper.ToObject(requestParam);
if (jd != null && jd.Keys.Contains("action"))
rc = jd["action"].ToString();
}
catch { }
}
}
}
}
catch (Exception ex) {
log.Error("GetAction", ex);
} return rc;
} } #region 接口处理区 #region 1001 获取 xxx列表
public class ListReqModel { [Required(ErrorMessage = "请上送需要类型!")]
[Range(0,5, ErrorMessage = "type参数值有误!")]
public int type { get; set; }
/// <summary>
/// 页码索引,从0开始
/// </summary>
[Required(ErrorMessage = "请上送页码索引!")]
[Min(, ErrorMessage = "pageIndex参数值有误!")]
public int pageIndex { get; set; } /// <summary>
/// 每页个数
/// </summary>
[Required(ErrorMessage = "请上送每页的个数!")]
[Min(, ErrorMessage = "pageSize参数值有误!")]
public int pageSize { get; set; } }
public class ListHandler : IndexHandler_Base64<ListReqModel> { public ListHandler() : base("ListHandler") { }
protected override BaseResponseResult DoWork(ListReqModel param) {
BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError);
int totalCount = ;
ProjectBLL biz = new ProjectBLL();
DataTable table = biz.GetList(param.type ,Index._thisUserId, out totalCount, param.pageIndex, param.pageSize); if (table != null && table.Rows.Count > ) {
rc.SetReturnData(new
{
//总个数
totalCount = totalCount,
list = table.AsEnumerable().Select(it => new
{
id = it["ID"].ToInt64(),
title = it["Title"].ToNormalString(),
frontCover = it["FrontCover"].ToNormalString()
}).ToList()
});
rc.SetResult(ErrorCode.Success, "成功!");
}
else if (totalCount > )
rc.SetResult(ErrorCode.NotExistPage, "当前页码不存在!");
else
rc.SetResult(ErrorCode.NoData, "没有数据哟!"); return rc;
}
}
#endregion #region 1002 获取xxx信息 public class ItemReqModel {
[Required(ErrorMessage = "请上送项目标识!")]
[Min(, ErrorMessage = "id参数值有误!")]
public long id { get; set; }
} public class ItemHandler : IndexHandler_Base64<ItemReqModel> {
public ItemHandler() : base("ItemHandler") { } protected override BaseResponseResult DoWork(ItemReqModel param) {
BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError);return rc;
}
} #endregion#endregion

ajax的使用方式:

//接入说明:
//1、请使用POST方式上传;
//2、请将参数组装为Json字符串,如:{'action':'xxx','xx':'xx'};
//3、Json字符串组装好以后,将其变为Base64字符串后上传。 //上送数据案例如下所示:
$.ajax({
type: "POST",
dataType: "json",
url: GetHost() + 'Index.ashx',
data: Base.encode('{"action":"1001","type":"' + type + '","pgIndex":' + pageIndex + ',"pgSize":' + pageSize + '}'),
contentType:"application/json",
success: function (data) {
//获取数据
if (data) {
//成功获取数据
if (data.returnValue == ) {
//处理数据
}
}
}
});

至此,完毕!以上的通用接口处理器采用了模板方法设计模式,尽量减少重复工作量。

说明:本篇文章为作者原创,如需转载,请说明来源及出处!

asp.net 自定义的模板方法接口通用类型的更多相关文章

  1. asp.net core参数保护之自定义要保护的参数类型

    asp.net core参数保护之自定义要保护的参数类型 Intro 为了实现 asp.net core 下的参数保护,扩展了asp.net core 中 DataProtection,可以自动化的保 ...

  2. ASP.NET交互Rest服务接口(Jquery的Get与Post方式)

    ASP.NET交互Rest服务接口(Jquery的Get与Post方式) 本文将通过一个简单的实例,介绍如何创建一个Rest服务接口,以及通过JQUERY去对它进行调用;主要采取两种方式分别为Get跟 ...

  3. 连表查询都用Left Join吧 以Windows服务方式运行.NET Core程序 HTTP和HTTPS的区别 ASP.NET SignalR介绍 asp.net—WebApi跨域 asp.net—自定义轻量级ORM C#之23中设计模式

    连表查询都用Left Join吧   最近看同事的代码,SQL连表查询的时候很多时候用的是Inner Join,而我觉得对我们的业务而言,99.9%都应该使用Left Join(还有0.1%我不知道在 ...

  4. 《连载 | 物联网框架ServerSuperIO教程》- 13.自定义视图显示接口开发,满足不同的显示需求

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  5. ABAP基本数据类型、通用类型

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  6. Android 代码库(自定义一套 Dialog通用提示框 )

          做Android开发五年了,期间做做停停(去做后台开发,服务器管理),当回来做Android的时候,发现很生疏,好些控件以前写得很顺手,现在好像忘记些什么了,总要打开这个项目,打开那个项目 ...

  7. ASP.NET中共有哪几种类型的控件?其中,HTML控件、HTML服务器控件和WEB服务器控件之间有什么区别

    ASP.NET的控件包括WEB服务器控件.WEB用户控件.WEB自定义控件.HTML服务器控件和HTML控件.HTML控件.HTML服务器控件和WEB服务器控件之间的区别如下所示.q      HTM ...

  8. ASP.NET WebAPI构建API接口服务实战演练

    一.课程介绍 一.王小二和他领导的第一次故事 有一天王小二和往常一下去上早班,刚吃完早餐刚一打开电脑没一会儿.王小二的领导宋大宝走到他的面前,我们现在的系统需要提供服务给其他内部业务系统,我看你平时喜 ...

  9. 使用ASP.NET Identity 实现WebAPI接口的Oauth身份验证

    使用ASP.NET Identity 实现WebAPI接口的Oauth身份验证   目前WEB 前后端分离的开发模式比较流行,之前做过的几个小项目也都是前后分离的模式,后端使用asp.net weba ...

随机推荐

  1. ORACLE 本地冷迁移

    需求:把oracle数据库的数据文件,redo文件,控制文件迁移到本地的其它目录. 1.测试环境: 操作系统redhat 6.3,数据库oracle 11.2.0.1.0 [root@dbtest1 ...

  2. Hadoop(三)手把手教你搭建Hadoop全分布式集群

    前言 上一篇介绍了伪分布式集群的搭建,其实在我们的生产环境中我们肯定不是使用只有一台服务器的伪分布式集群当中的.接下来我将给大家分享一下全分布式集群的搭建! 其实搭建最基本的全分布式集群和伪分布式集群 ...

  3. 编译LNMP之nginx+php-fpm

    html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...

  4. 坑中速记整理! 使用 kotlin 写第一个 ReactNative Android 模块

    Kotlin 和 Swift, 两大新宠! 借 ReactNative 熟悉下 kotlin 的用法,不料掉坑里面了.昨晚花了大半夜,趁这会儿思路清晰,把涉及到的一些关键信息,迅速整理下. 最佳的使用 ...

  5. zoj 1526 Big Number 数学

    Big Number Time Limit: 10 Seconds      Memory Limit: 32768 KB In many applications very large intege ...

  6. Java面向对象 IO (四)

     Java面向对象  IO  (四) 知识概要:                 (1)打印流 (2)序列流 SequenceInputStream (3)ObjectInputStream与Ob ...

  7. WPF---Effect效果

    在 WPF 中,可以使用 BitmapEffect 对象为每一个 Visual 对象生成各种各样的效果,一个 Visual 对象可以设置一种或多种 BitmapEffect 效果,WPF 内置了几种效 ...

  8. [js高手之路]打造通用的匀速运动框架

    本文,是接着上文[js高手之路]匀速运动与实例实战(侧边栏,淡入淡出)继续的,在这篇文章的最后,我们做了2个小实例:侧边栏与改变透明度的淡入淡出效果,本文我们把上文的animate函数,继续改造,让变 ...

  9. This application failed to start because it could not find or load the Qt platform plugin "windows" 的问题原因以及解决方案

    1. 问题原因非常简单,经过各种百度,都没有找到解决方案,在此做一个记录备用. 2.原因就在于,项目目录使用了中文路径,然后出现了这个问题. 3.我是在使用 syncfusion 下的HTML 转PD ...

  10. UWP xaml 圆形头像

    圆形头像 去掉黑边 拖动打开图形 圆形头像 现在很多软件都喜欢使用圆形头像 win10 uwp使用圆形头像很简单 <Ellipse Width="200" Height=&q ...