博客 (60)

  1. 微信的 H5 页面前端引导到:

    https://api.magcloud.cc/magshare/{siteId}?jump_url={jump_url}&content_url={content_url}

    各参数含义见官方文档:http://doc.magcloud.net/325339

    content_url 地址(一般仍为活动页面)接收参数 magshareredirect,值为 1 则跳转到自定义的引导页。

  2. 自定义引导页按 iOS 和安卓显示图文和箭头,引导用户点击右上角菜单,在浏览器中打开。

  3. 在浏览器 H5 中引导打开:

    {siteId}://pagejump?jump_url={jump_url}

    各参数含义见第 1 步中的文档。

    这里涉及一个“若未安装则引导安装”的过程,可以选择下方合适的一种方式实现:

    • A 标签的 href 填入下载地址,onclick 事件 location 到 App

    • onclick 事件 location 到 App,再用 setTimeOut location 到下载地址

    • 若仍无法实现预期效果,可以在弹出层中放置“未安装,前往下载”和“已安装,立即打开”两个按钮,让用户自己选择

  4. 若下载地址为应用宝,那么在 iOS 中可能会出现“打开 App 后继而自动打开 App Store”的情况,可以自建中转的 App 介绍页引导到 App Store 或应用宝。

  5. 分别实现微信和 MAGAPP 客户端的分享

xoyozo 6 年前
6,174

本文介绍 ASP.NET 的 Swagger 部署,若您使用 ASP.NET Core 应用程序,请移步 ASP.NET Core Web API Swagger 官方文档:

https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/first-web-api?view=aspnetcore-5.0&tabs=visual-studio

https://github.com/domaindrivendev/Swashbuckle.AspNetCore


安装

NuGet 中搜索安装 Swashbuckle,作者 Richard Morris


访问

http://您的域名/swagger


配置


显示描述

以将描述文件(xml)存放到项目的 bin 目录为例:

  1. 打开项目属性,切换到“生成”选项卡

  2. 在“配置”下拉框选择“所有配置”

  3. 更改“输出路径”为:bin\

  4. 勾选“XML 文档文件”:bin\******.xml,(默认以程序集名称命名)

  5. 打开文件:App_Start/SwaggerConfig.cs

  6. 取消注释:c.IncludeXmlComments(GetXmlCommentsPath());

  7. 添加方法:

    public static string GetXmlCommentsPath()
    {
        return System.IO.Path.Combine(
            System.AppDomain.CurrentDomain.BaseDirectory,
            "bin",
            string.Format("{0}.xml", typeof(SwaggerConfig).Assembly.GetName().Name));
    }

    其中 Combine 方法的第 2 个参数是项目中存放 xml 描述文件的位置,第 3 个参数即以程序集名称作为文件名,与项目属性中配置一致。

如遇到以下错误,请检查第 2、3、4 步骤中的配置(Debug / Release)

500 : {"Message":"An error has occurred."} /swagger/docs/v1


使枚举类型按实际文本作为参数值(而非转成索引数字)

  1. 打开文件:App_Start/SwaggerConfig.cs

  2. 取消注释:c.DescribeAllEnumsAsStrings();

xoyozo 6 年前
3,876

ASP.NET Web API 返回 Unauthorized 401 未授权代码:

return Unauthorized(new AuthenticationHeaderValue("getUserInfo"));

响应的头部信息是这样的:

image.png

以微信小程序中获取 header 的 www-authenticate 为例,不同操作系统中获取的 key 是不一样的:


iOS:

image.png

Android:

image.png

微信开发者工具:

微信图片_20190716125001.png

所以,小程序端获取该值,暂时使用以下代码吧:

let www_authenticate;
if (!www_authenticate) {
	www_authenticate = res.header['Www-Authenticate']; // iOS
}
if (!www_authenticate) {
	www_authenticate = res.header['WWW-Authenticate']; // Android / 微信开发者工具
}
if (!www_authenticate) {
	www_authenticate = res.header['www-authenticate']; // ASP.NET Web API 原始输出
}
if (!www_authenticate) {
	www_authenticate = res.header['WWW-AUTHENTICATE']; // 其它情况
}
xoyozo 6 年前
7,538

百度地图天地图
初始化地图var map = new BMap.Map("allmap");var map = new T.Map("allmap");
将覆盖物添加到地图addOverlay(overlay: Overlay)addOverLay(overlay:OverLay)
从 Map 的 click 事件中获取坐标点

map.addEventListener("click", function (e) {

    Point = e.point;

}

map.addEventListener("click", function (e) {

    LngLat = e.lnglat;

}

坐标点new BMap.Point(lng: Number, lat: Number)new T.LngLat(lng: Numbe, lat: Number)
像素点new BMap.Pixel(x: Number, y: Number)new T.Point(x: Number, y: Number)
文本标注
new BMap.Label(content: String, {
    offset: Size,
    position: Point
})
new T.Label({
    text: string, 
    position: LngLat, 
    offset: Point
})
设置文本标注样式
setStyle(styles: Object)每个样式使用单独的 set 方法实现
图像标注
new BMap.Marker(Point, {
    offset: Size,
    icon: Icon,
    enableMassClear: Boolean,
    enableDragging: Boolean,
    enableClicking: Boolean,
    raiseOnDrag: Boolean,
    draggingCursor: String,
    rotation: Number,
    shadow: Icon,
    title: String
})
new T.Marker(LngLat, {
    icon: Icon,
    draggable: boolean,
    title: string,
    zIndexOffset: number,
    opacity: number
})
为图像标注添加文本标注Marker.setLabel(label: Label)

无。

可借用 title(鼠标掠过显示)属性来实现,并通过 Marker.options.title 来获取值

从图像标注获取坐标点Point = Marker.getPosition();
LngLat = Marker.getLngLat();
绘制折线
new BMap.Polyline(points: Array<Point>, {
    strokeColor: String,
    strokeWeight: Number,
    strokeOpacity: Number,
    strokeStyle: String,
    enableMassClear: Boolean,
    enableEditing: Boolean,
    enableClicking: Boolean,
    icons: Array<IconSequence>
})
new T.Polyline(points:Array<LngLat>, {
    color: string,
    weight: number,
    opacity: number,
    lineStyle: string
})
绘制多边形
new BMap.Polygon(points: Array<Point>, {
    strokeColor: String,
    fillColor: String,
    strokeWeight: Number,
    strokeOpacity: Number,
    fillOpacity: Number,
    strokeStyle: String,
    enableMassClear: Boolean,
    enableEditing: Boolean,
    enableClicking: Boolean
})
new T.Polygon(points:Array<LngLat>,{
    color: string,
    weight: number,
    opacity: number,
    fillColor: String,
    fillOpacity: number,
    lineStyle: string
})
设置多边形的点数组Polygon.setPath(points: Array<Point>)Polyline.setLngLats(lnglats: Array<LngLat>)
设置地图视野Map.setViewport(view: Array<Point> | Viewport, viewportOptions: ViewportOptions)Map.setViewport(view: Array<LngLat>)
xoyozo 6 年前
7,830

之前已经介绍了百度编辑器在 ASP.NET 环境下的配置,本文继续补充改进其将图片等附件上传到阿里云 OSS。

首先从阿里云控制台下载相关 SDK,并阅读相关的 API 文档。链接:https://promotion.aliyun.com/ntms/act/ossdoclist.html


改造上传代码


打开文件 UploadHandler.cs,在 Process() 方法中找到:

File.WriteAllBytes(localPath, uploadFileBytes);

在其下方插入代码:

client.PutObject(bucketName, "upload/ueditor/" + savePath, localPath);

实现图标按钮上传和 HTML5 拖放上传。

PutObject() 的第 2 个参数是 OSS 中的图片路径,第 3 个参数是图片在网站目录下的磁盘路径。


打开文件 CrawlerHandler.cs,在 Fetch() 方法中找到:

File.WriteAllBytes(savePath, bytes);

在其下方插入代码:

client.PutObject(bucketName, "upload/ueditor/" + ServerUrl, savePath);

实现粘贴板图片上传。

同样注意两个参数变量。


修改路径前缀


上传成功后,编辑器会拼装路径到 HTML 代码中,这个前缀是在 config.json 文件中配置的,具体找到以 UrlPrefix 结尾的属性,如 imageUrlPrefix,进行相应的修改即可:

"imageUrlPrefix": "//oss.xoyozo.net/upload/ueditor/", /* 图片访问路径前缀 */

路径以“//”开头能更好地兼容 https。

xoyozo 6 年前
7,270

前言:

ChatGPT 给了 3 条建议:

  1. 在应用程序中正确释放数据库连接。确保在使用完数据库连接后,将其关闭并将其返回到连接池中。您可以使用 using 语句来确保连接在使用完毕后被正确释放。

  2. 调整连接池的大小。默认情况下,连接池的最大大小为 100。如果您的应用程序需要更多的连接,则可以增加连接池的大小。您可以在连接字符串中设置 Max Pool Size 属性来调整连接池的大小。

  3. 调整连接池的超时时间。默认情况下,连接池中的连接在 30 秒钟内未使用时将被关闭。如果您的应用程序需要更长的连接时间,则可以增加连接池的超时时间。您可以在连接字符串中设置 Connection Lifetime 属性来调整连接池的超时时间。

亲测有效,尤其是第 3 条,原因是 Connection Lifetime 的默认值是 0,即没有超时限制。

—— 2023.5

一般地,我们使用 EF 连接数据库前会先初始化一个数据库上下文:

dbEntities db = new dbEntities();

虽然 ASP.NET 会在查询完毕后自动关闭该连接,但是在什么情况下回收等都是不确定的,所以会导致 MySQL 中出现很多 Sleep 的连接(执行命令 SHOW FULL PROCESSLIST 可见),占用数据库的连接数,除非主动调用 Dispose():

db.Dispose();

官方建议的写法是使用 using 语法:

using (dbEntities db = new dbEntities())
{
}

using 会自动调用 Dispose()。这样对减少连接数是很有效的,但官方提示为了提高下一次连接的速度,并不会完全关闭所有连接。

C# 8 建议写法:

using dbEntities db = new dbEntities();

在实际项目中(该项目有 500+处数据库连接)测试结果,在不执行 Dispose() 时稳定为 140 个左右连接数,使用 using 或 Dispose() 后稳定变为 40 个左右。


如果不小心在 using 外部或 Dispose() 后再次对该上下文执行查询操作会出现异常:

无法完成该操作,因为 DbContext 已释放。

此 ObjectContext 实例已释放,不可再用于需要连接的操作。

所以要避免出现这种情况。这里还有一种另类的解决方法(不建议),根据上下文的特性,只要在 using 内查询一次(譬如视图中需要用到的导航属性,即外键关联的表),就可以在外部使用这个属性。


(建议)在 ASP.NET MVC 或 Web API 项目中,如果一个控制器中仅在 Action 外部定义一个 DbContext,那么,只要重写该控制器的 Dispose() 方法即可:

private dbEntities db = new dbEntities();

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        db.Dispose();
    }
    base.Dispose(disposing);
}

上下文使用 private 修饰即可。


参考:SQL Server 连接池 (ADO.NET)

xoyozo 6 年前
6,487

前提:

若需获取用户 unionid,则小程序必须已绑定到微信开放平台。


  1. 小程序调用 wx.login(),将 code 发送到开发者服务器

  2. 开发者服务器请求微信接口 code2Session,获得 session_keyopenid,有机会获得 unionid参 UnionID 机制说明)。因此无需任何用户授权即可获取 openid

  3. 访问须要已获取用户信息的页面时,可通过小程序端使用 button + getUserInfo 的方式请求用户授权

  4. 访问须要已获取手机号码的页面时,可通过小程序端使用 button + getPhoneNumber 的方式请求用户授权

  5. code2session、getUserInfo、getPhoneNumber 等接口返回数据存入数据库 dt_weapp_user

  6. 服务端的任何接口均返回口令状态包(含自定义会话口令等信息),小程序端应保存于 storage

  7. 请求 getUserInfo 后应判断 detail.errMsg 是否为 getUserInfo:ok,请求 getPhoneNumber 后应判断 detail.errMsg 是否为 getPhoneNumber:ok

  8. 授权窗口的弹出情况此文

  9. 口令状态包是用来传递到客户端用来标识用户唯一凭证的,包含字段:会话口令(自定义用户凭证)、会话口令有效时间(秒)、是否已获取 unionid 或昵称(视业务需求)、是否已获取手机号码uid(可选),以及其他用来标识业务身份字段。服务端可根据会话口令从数据库中获取该用户的所有已知信息。

    一般情况下,当已获取手机号码时,uid 必有值,否则,uid 必为空。因此 uid 不是必须传递到客户端的。

    通过 code2Session 获得的 session_key、以及 openidunionid手机号码等机密信息不允许包含在口令状态包中并缓存在客户端,客户端需要用到手机号码时单独调用接口获取。

  10. 若业务不要求以手机号码作为用户唯一标识,那么口令状态包中不需要包含是否已获取手机号码,仅 uid 即可。在客户端访问须要业务用户登录的页面时,也仅判断 uid 有值即可。


流程图:


微信小程序授权登录获取 unionid、用户信息流程.png


为更好地适应 2017.7.19 发布的《小程序内用户帐号登录规范调整和优化建议》开发者服务器提供的所有业务逻辑接口的返回值中都应包含口令状态包,约定有以下相关的返回值和处理动作(返回值按个人喜好来定):

口令状态接口返回ASP.NET Web API 语句处理动作
access_token 空或失效401 状态码return Unauthorized();login() + code2Session,获取 openid,生成 access_token
access_token 有效,但未获取 uniond 或用户信息401 状态码,http 头 WWW-Authenticate 中标注 getUserInforeturn Unauthorized(new AuthenticationHeaderValue("getUserInfo"));getUserInfo()
access_token 有效,但未获取手机号码(即未找到业务用户)401 状态码,http 头 WWW-Authenticate 中标注 getPhoneNumberreturn Unauthorized(new AuthenticationHeaderValue("getPhoneNumber"));getPhoneNumber()
xoyozo 6 年前
9,765

功能
适用场景
wx.switchTab跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。在应用内的某个页面上处理事务完毕,需要跳转到 tabbar 页面时使用
wx.reLaunch关闭所有页面,打开到应用内的某个页面。当在任何页面判断登录失效时跳转到授权登录页(当所有页面都需要登录后才能访问的情况)
wx.redirectTo关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面。跳转到非 tabbar 页面,无返回按钮,目标页面必须具有跳转到小程序其它页面的功能
wx.navigateTo保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。跳转到非 tabbar 页面,有返回按钮
wx.navigateBack关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages 获取当前的页面栈,决定需要返回几层。返回


xoyozo 6 年前
3,992

未接受或拒绝过时发起已允许后再发起已拒绝后再发起
wx.getUserInfo
不弹,直接获取数据
wx.getPhoneNumber
wx.getLocation不弹,直接获取数据不弹,无法获取数据,必须引导 wx.getSetting

其它 scope 会在用到时补充……

“允许后再发起,不弹”的 scope 可在“关于XXX小程序”右上角三点按钮中的“设置”来设置是否允许。

xoyozo 6 年前
4,442

微信小程序 login 后,在用 code 换 session 时,如果小程序绑定了微信开放平台,应该会返回 unionid。

文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/code2Session.html

但实际上部分用户仍然是没有 unionid 的,把上述接口返回的数据记录下来如:

image.png

仔细阅读官方 UnionID 机制说明,通过用户授权 wx.getUserInfo 才会保证有 unionid 返回,否则只会在“用户关注了绑定同一微信开放平台的同主体的公众号”等情况下才会返回 unionid。


确保获取 unionid 流程参此文

xoyozo 6 年前
7,726