博客 (5)

iPhone 或安卓上传的照片,可能因横向或纵向拍摄,导致上传后照片显示成 90 度、180 度、270 度角旋转,使用 C# 处理该照片时同样会遇到相同的问题。

解决的方案:

客户端使用 base64 显示图片,可按正常方向显示,且不通过服务端即时显示。关键代码:

$('#file_input').change(function (e) {
    if (e.target.files.length == 0) { return; }
    // file 转换为 base64
    var reader = new FileReader();
    reader.readAsDataURL(e.target.files[0]);
    reader.onload = function (event) {
        $('#myImg').attr('src', event.target.result).css({ width: 'auto', height: 'auto' });
    }
});
$('#myImg').on('load', function () {
    console.log('图片加载完成')
});

但 base64 字符串传递到服务端受请求大小限制(一般比直接 POST 文件要小)。所以显示图片的同时应即时使用 POST 方式上传图片(如 jquery.fileupload.js)。

当然如果是在微信中打开网页,使用 JS-SDK 的 wx.chooseImage() 返回的 localId 也可以在 <img /> 中正常显示,然后使用 wx.uploadImage() 上传到微信服务器并获取 serverId,在通过获取临时素材接口取回图片文件。

服务端如果需要处理该图片(缩放、裁切、旋转等),需要先读取照片的拍摄角度,旋正后,再进行处理,相关 C# 代码:

/// <summary>
/// 调整照片的原始拍摄旋转角度
/// </summary>
/// <param name="img"></param>
/// <returns></returns>
private Image ImageRotateFlip(Image img)
{
    foreach (var prop in img.PropertyItems)
    {
        if (prop.Id == 0x112)
        {
            switch (prop.Value[0])
            {
                case 6: img.RotateFlip(RotateFlipType.Rotate90FlipNone); break;
                case 3: img.RotateFlip(RotateFlipType.Rotate180FlipNone); break;
                case 8: img.RotateFlip(RotateFlipType.Rotate270FlipNone); break;
            }
            prop.Value[0] = 1;
        }
    }
    return img;
}
xoyozo 3 年前
2,304

今天发现在开发新项目时,微信分享失效了,开启 debug = true 后,在微信开发工具中提示:

{"errMsg":"updateTimelineShareData:fail, the permission value is offline verifying"}

在真机上提示:

{"errMsg":"config:ok"}

{"errMsg":"updateAppMessageShareData:ok"}

{"errMsg":"updateTimelineShareData"}

仍然找不出原因。

尝试打开已上线的项目,分享功能均正常。

偶然发现,当打开使用 JS-SDK 分享后的图文框的页面,能够正常分享,而直接点开链接的页面,没有分享按钮。

这难免不让人联想到最近GJ出的新政,新版微信已经开放淘宝、抖音等平台的链接打开权限,只是当点击链接的时候会出现一个风险提示页面:

d9dd5a9e715606dd77f4b4b47ac44ab.jpg

而一旦由这个页面进入的网页,分享功能均不能正常。

另外还测试了以下几种情况的分享功能也能正常:

公众号推送的图文或链接、公众号菜单、个人发送到公众号的链接、通过扫一扫打开的页面等。

-- 2021.9

xoyozo 3 年前
6,147

* 若无特殊注明,本文中的“手机”包含平板电脑


判断网页是否在移动设备中打开


Web Form:

public static bool IsMobileDevice
{
    get
    {
        return (HttpContext.Current.Request.Browser?.IsMobileDevice ?? false)
            || (HttpContext.Current.Request.UserAgent?.Contains("Mobile") ?? false);
    }
}


ASP.NET Core:

好像还没有找到,可能是因为随着响应式布局的流行以及电脑和手机之间的界线越来越不明朗,取消了这个判断?


判断网页是否在微信浏览器中打开(宽松)


Web Form:

public bool IsInWechat
{
    get
    {
        return HttpContext.Current.Request.UserAgent?.Contains("MicroMessenger") ?? false;
    }
}


ASP.NET Core:

需注入 IHttpContextAccessor

public bool IsInWechat
{
    get
    {
        string ua = httpContextAccessor.HttpContext.Request.Headers[HeaderNames.UserAgent];
        return ua?.Contains("MicroMessenger") ?? false;
    }
}


判断网页是否在微信浏览器中打开(严谨)


使用 JS-SDK 网页授权来判断




xoyozo 4 年前
2,883
  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 5 年前
3,971

在开发微信中的网页时,会遇到一些域名相关的配置:

① 公众号设置 - 功能设置 - 业务域名
② 公众号设置 - 功能设置 - JS接口安全域名
③ 接口权限 - 网页授权获取用户基本信息
④ 商户平台 - 产品中心 - 开发配置 - JSAPI支付授权目录
⑤ 小程序 - 开发 - 开发设置 - 业务域名
⑥ 公众号开发 - 基本设置 - IP白名单

 

第①种 业务域名:相对不重要,只是用来禁止显示“防欺诈盗号,请勿支付或输入qq密码”提示框,可配置 3 个二级或二级以上域名(个人理解是“非顶级域名”,即填写了 b.a.com 的话,对 c.b.a.com 不起作用,待测)。

我们网站的域名和公众号是没有绑定关系的,那么你在打开一个(可能是朋友分享的)网页时,跟哪个公众号的配置去关联呢,答案是 JS-SDK。

测试结果:由于一个月只有3次修改机会,这次先测二级域名,有效;再测顶级域名,有效;删除所有,仍有效。所以应该是缓存作用。过几天再试,然后下个月先测顶级域名,来确定直接填写顶级域名是否对所有二级域名有效。

 

第②种 JS接口安全域名:是配置所配置的域名下的网页可调用 JS-SDK。可配置 5 个一级或一级以上域名(个人理解是“任何级域名”,即填写了 b.a.com 对 c.b.a.com 也有效,但对 c.z.a.com 无效,待测。如果我们拥有顶级域名对应网站的控制权(上传验证文件到网站根目录),直接填写顶级域名即可)。
使用 JS-SDK 的每个网页都必须注入配置信息(wx.config),而之前必须获取 jsapi_ticket,jsapi_ticket api 的调用次数非常有限,必须全局缓存。而获取 jsapi_ticket 之前必须先获取 access_token,同样需要全局缓存。所以,我们专门做个接口,功能是传入需要使用 JS-SDK 的网页的 url,输出 wx.config 需要用到的配置信息,来实现在不同网页(网站)使用 JS-SDK。

 

第③种 网页授权域名:这是已认证的服务号才能享有的特权,主要作用是获取用户在该服务号中的 openidunionid。可配置 1 个 2 个回调域名(可填写任意级别的域名,但仅对该域名的网页(网站)有效,若填写了 a.comb.a.com 是无效的)。

因此,如果我们需要在不同二级域名甚至不同顶级域名下的网页(以下称之为活动页面)实现用户授权,需要做一个统一的代理授权页面(回调域名当然是填写这个页面所在的域名),引导用户依次打开:微信授权页面 - 代理授权页面 - 活动页面,根据开发说明文档,具体实现如下:

当用户第一次打开活动页面时,引导打开微信授权页面(https://open.weixin.qq.com/connect/oauth2/authorize),其中参数 redirect_uri 指定回调地址,即代理授权页面地址,参数 state 指定活动页面地址。微信授权页面返回 code 和 state,code 作为换取网页授权 access_token 的票据。到这一步,本来可以由代理授权页面直接拿这个 code 去换取 access_token、openid 和 unionid 了,但是由于当前用户还在 302 重定向过程中,将这些信息带入到活动页面时势必导致信息泄露,所以这里将 code 追加到 state 指定的网址上后重定向到活动页面,活动页面拿到 code 再通过服务器端向代理授权页面所在服务器请求 openid 和 unionid,并将它们保存于 Session 中视为用户登录。这样,服务号的 appid 和 secret 也能得到保护。代理授权页面请求的微信服务器接口地址是 https://api.weixin.qq.com/sns/oauth2/access_token

注:网页授权 access_token 不同于 JS-SDK 中使用的全局唯一接口调用凭据 access_token,没有请求次数限制。 

流程既然通了,实现逻辑可以这样设定:

活动页面首先判断 Session 中是否有 openid 或 unionid,若有表示已授权登录;没有再判断地址栏是否有 code 参数,若有则调用代理授权页面所在服务器的接口,用 code 换 openid 和 unionid;没有则直接重定向到代理授权页面,带上 state。用 code 换 openid 和 unionid 时若成功则保存至 Session,若失败则仍然重定向到代理授权页面,带上 state,特别注意 state 中的活动页面地址确保没有 code 参数。


第④种 JSAPI支付授权目录:涉及到微信支付时用到,顾名思义是固定某一个网站内的某个目录,以“/”结尾。最多可添加 5 个。如果支付页面在目录 https://www.a.com/b/ 下,那么可以填写 https://www.a.com/b/ 或 https://www.a.com/(建议后者),暂未测试填写 https://a.com/ 会不会起作用。涉及支付安全,建议设置支付页面的最深一层不可写的目录,以防目录内被上传后门文件带来的安全隐患。


第⑤种 小程序业务域名:任何需要在小程序的 web-view 组件中打开的网页,都必须配置小程序业务域名,限制 20 个。该域名要求必须 https,可填入“任何级域名”(建议填顶级域名,即填写了 a.com 对 b.a.com 也有效。如果我们拥有顶级域名对应网站的控制权(上传验证文件到网站根目录),直接填写顶级域名即可)。


第⑥种 IP白名单:仅填写管理全局 access_token 的中控服务器的 IP。

 

总结:在上述自定义接口部署完成后,如果微信中的网页想获取用户的 unionid,则不需要配置域名,直接使用统一的代理授权即可;如果需要使用 JS-SDK 功能,如分享、上传等等,则需要配置 JS 接口安全域名;如果有表单,最好配置一下业务域名。


本文系个人经验总结,部分结果未经证实,欢迎指正!QQ:940534113

xoyozo 8 年前
6,783