微信公众号网页授权代码优化过程(三)

上篇博客已完成了获取openid的功能,但也遗留了问题,本篇我们将会改进它。

与版本一的变动说明

这个版本(版本二)中我们将使用SpringMVC中比较少见的一种注解@ModelAttribute,至少我之前并未用过它,也没有过多关注过,但稍微了解了之后发现它标注在方法上时就相当于Junit中的@Before注解,被@Test标注的方法在执行之前会执行被@Before标注的方法。当然,我隐隐感觉@ModelAttribute还有其它用法……

版本二中我们舍弃了GotoController类,它的名字也太不友好了。但是我们接着引入两个新类:

  • WxOAuth2Controller 该类中暂只有一个方法beforeH5Page(),它被@ModelAttribute所标注,所以——它们被业务Controller所继承,我们现有的GoodsController会转而继承WxOAuth2Controller。原理是当我们请求/goods/list时,会先执行beforeH5Page(),本方法中会处理原来GotoController#to()里要处理的工作,它先判断session中是否已有openid,有的话不做处理,没有的话就把要发向微信授权认证的链接(该链接中原有的redirect_uri要指向WxRedirectController的callback()方法)读出来保存到session中,然后调用WxRedirectController#toOAuth2()方法发送授权认证请求。
  • WxRedirectController
    • 该类中的方法有个方法叫toOAuth2(),它把WxOAuth2Controller#beforeH5Page()放在session中的链接读出来,并进行重定向到微信。微信处理完毕后会请求到WxOAuth2Controller#beforeH5Page()中配置的redirect_uri,而该路径对应WxRedirectController的第二个方法callback()。
  • callback()方法接收code参数,并根据此code请求微信获得openid,再把openid放在session中。

上面的话可能有点多,也有人可能会考虑为什么WxOAuth2Controller和WxRedirectController 不能合并成一个类,现实导致WxOAuth2Controller类要与WxRedirectController类不能合并,我刚开始试的时候就把两个类合在一起,导致callback()永远无法执行,因为假如合并成一个类,在执行callback()前会先执行被@ModelAttribute所标注的beforeH5Page()方法,从而产生一个无限循环,^_^。

运行,出现问题

同样发"域名/weixin-oauth-code-optimization-demo/goods/list"到公众号,再点击链接发现页面加载了多次(大于2次,网络越慢越明显),看下GoodsController#list()方法,发现openid打印了两次,第一次openid为空,第二次正常,我们再点击右上角在弹出层中点刷新,发现这次只打印了一次openid,并且版本一中的那个问题也就此解决了(可去回顾一下版本一中遗留的那个问题)。原因也很好解释,被@ModelAttribute所标注的方法在执行完成之后会接着往下执行到GoodsController#list()方法,此时session中还没有openid,所以出现了null,我们在这里演示是没有问题的,但是业务中通常都会使用这个openid去查询数据库的,openid为空时去查询数据库就显示没有必要了。

无论如何,我们还是往前走一步了,解决了一个问题,尽管又产生了一个问题…… 下面会有改进的方法,但代码会有变动,所以会有两份代码A和B,先提供第一份https://gitee.com/valuetodays/weixin-oauth-code-optimization-demo/tree/tag-2.0-a

改进方案

我当时改进的办法是,其它是从一个low的解决办法走向了另一个low的解决办法,就是在GoodsController#list()方法一开始先判断session中有无openid,有的话进行业务,没有的话就去做授权认证;这个时候WxOAuth2Controller#beforeH5Page()就不要再发送授权认证请求了,它只把链接放在session中就好。这就是第二份代码B,地址为https://gitee.com/valuetodays/weixin-oauth-code-optimization-demo/tree/tag-2.0-b

然后程序就这样美好的运行了。

添加新模块……

目前为止,系统运行地还不错。但是这个时候添加了一个订单列表页面。和商品列表页面拥有同样简单的逻辑(我们的第一个页面就叫商品列表页面,尽管它没有显示一条商品信息)。这时候,我们只需把GoodsController复制一份,把请求路径和跳转页面修改一下就好了。此时的两个功能的list()是一样的,如下:

    @GetMapping("list")
    public String list(HttpServletRequest request, Model model) {
        Object openidTokenObj = request.getSession().getAttribute(WxRedirectController.WX_OPENID_TOKEN);
        if (openidTokenObj == null) {
            wxRedirectController.toOAuth2();
        } else {
            LOG.debug("openid: " + openidTokenObj);
            model.addAttribute("openid", openidTokenObj);
            return "goods/list";
        }
        return "error";
    }

看着还好,没什么问题,但是,我再添加一个商品详情页面的话,是得在GoodsController中添加一个detail()方法的,然而这个方法也得使用openid,然后,代码中又出现了久违的如下片段:

        Object openidTokenObj = request.getSession().getAttribute(WxRedirectController.WX_OPENID_TOKEN);
        if (openidTokenObj == null) {
            wxRedirectController.toOAuth2();
        } else {
            ......
        }

订单模块中也再添加一个详情页面的话呢,再添加一个个人中心模块呢,再…… 不用再想了。上面的做法是不太友好的。由此我就在想怎么解决这个问题,即业务代码中不用判断是否有openid,而是程序走到这里就一定会有。我就在csdn上发贴询问以期找到解决方案,最终找到了解决方案——使用Servlet的过滤器或SpringMVC的Interceptor即可。

代码侵入

不知大家看到没有,版本二的b版本已经能很好地运行了,但是它存在一个很大的问题,业务代码中的Controller类要继承WxOAuth2Controller 并且要使用WxRedirectController ,这两个类应该和业务无关,只有架构有关的啊。令人兴奋的是,在版本三中我们将把代码变得更加像样,且不再侵入,不让代码继承这个,也不让你使用那个,当然,WxOAuth2Controller和WxRedirectController这两个类少不了要被删除的。

本篇到此为此吧。

猜你喜欢

转载自my.oschina.net/valuetodays/blog/1627497