阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

编写UI

37次阅读
没有评论

共计 3366 个字符,预计需要花费 9 分钟才能阅读完成。

我们已经实现了 API 系统、交易系统、定序系统、行情系统和推送系统,最后就差一个 UI 系统,让用户可以登录并通过浏览器下订单。UI 系统就是一个标准的 Web 系统,相对比较简单。

UI 系统本质上是一个 MVC 模型的 Web 系统,我们先引入一个视图的第三方依赖:

<dependency>
    <groupId>io.pebbletemplates</groupId>
    <artifactId>pebble-spring-boot-starter</artifactId>
    <version>${pebble.version}</version>
</dependency>

ui.yml 加入最基本的配置:

pebble:
  prefix: /templates/
  suffix: .html

注意到视图页面都放在 src/main/resources/templates/ 目录下。编写MvcController,实现登录功能:

@Controller
public class MvcController extends LoggerSupport {// 显示登录页
    @GetMapping("/signin")
    public ModelAndView signin(HttpServletRequest request) {if (UserContext.getUserId() != null) {return redirect("/");
        }
        return prepareModelAndView("signin");
    }

    // 登录
    @PostMapping("/signin")
    public ModelAndView signIn(@RequestParam("email") String email, @RequestParam("password") String password, HttpServletRequest request, HttpServletResponse response) {try {UserProfileEntity userProfile = userService.signin(email, password);
            // 登录成功后设置 Cookie:
            AuthToken token = new AuthToken(userProfile.userId, System.currentTimeMillis() + 1000 * cookieService.getExpiresInSeconds());
            cookieService.setSessionCookie(request, response, token);
        } catch (ApiException e) {// 登录失败:
            return prepareModelAndView("signin", Map.of("email", email, "error", "Invalid email or password."));
        } catch (Exception e) {// 登录失败:
            return prepareModelAndView("signin", Map.of("email", email, "error", "Internal server error."));
        }
        // 登录成功跳转:
        return redirect("/");
    }
}

登录成功后,设置一个 Cookie 代表用户身份,以 userId:expiresAt:hash 表示。由于计算哈希引入了HmacKey,因此,客户端无法伪造 Cookie。

继续编写 UIFilter,用于验证 Cookie 并把特定用户的身份绑定到UserContext 中:

public class UIFilter {@Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {// 查找 Cookie:
        AuthToken auth = cookieService.findSessionCookie(req);
        Long userId = auth == null ? null : auth.userId();
        try (UserContext ctx = new UserContext(userId)) {chain.doFilter(request, response);
        }
    }
}

我们再编写一个ProxyFilter,它的目的是将页面 JavaScript 对 API 的调用转发给 API 系统:

public class ProxyFilter {@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {Long userId = UserContext.getUserId();
        // 构造一次性 Token:
        String authToken = null;
        if (userId != null) {AuthToken token = new AuthToken(userId, System.currentTimeMillis() + 60_000);
            authToken = "Bearer" + token.toSecureString(hmacKey);
        }
        // 转发到 API 并读取响应:
        String responseJson = null;
        try {if ("GET".equals(request.getMethod())) {Map<String, String[]> params = request.getParameterMap();
                Map<String, String> query = params.isEmpty() ? null : convertParams(params);
                responseJson = tradingApiClient.get(String.class, request.getRequestURI(), authToken, query);
            } else if ("POST".equals(request.getMethod())) {responseJson = tradingApiClient.post(String.class, request.getRequestURI(), authToken,
                        readBody(request));
            }
            // 写入响应:
            response.setContentType("application/json;charset=utf-8");
            PrintWriter pw = response.getWriter();
            pw.write(responseJson);
            pw.flush();} catch (ApiException e) {// 写入错误响应:
            writeApiException(request, response, e);
        } catch (Exception e) {// 写入错误响应:
            writeApiException(request, response,
                    new ApiException(ApiError.INTERNAL_SERVER_ERROR, null, e.getMessage()));
        }
    }
}

ProxyFilter 挂载到 /api/*,通过 UI 转发请求的目的是简化页面 JavaScript 调用 API,一是不再需要跨域,二是 UI 已经经过了登录认证,转发过程中自动生成一次性 Token 来调用 API,这样 JavaScript 不再关心如何生成Authorization 头。

下面我们就可以开始编写页面了:

  • signin.html:登录页;
  • signup.html:注册页;
  • index.html:交易页。

页面功能主要由 JavaScript 实现,我们选择 Vue 前端框架,最终实现效果如下:

编写 UI

最后,在后台注册时,如果检测到本地开发环境,就自动调用内部 API 给用户添加一些资产,否则新注册用户无法交易。

参考源码

可以从 GitHub 或 Gitee 下载源码。

GitHub

小结

UI 系统是标准的 Web 系统,除了注册、登录外,主要交易功能均由页面 JavaScript 实现。UI 系统本身不是交易入口,它通过转发 JavaScript 请求至真正的 API 入口。

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计3366字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中