14、开发 Emoji 日记应用:功能实现与服务集成

开发 Emoji 日记应用:功能实现与服务集成

1. 页面加载函数更新

首先,我们需要更新页面加载时执行的函数。将页面加载时执行的函数(就在 signupClicked 之前)更新为如下代码:

$(function() { 
  window.emojiPicker = new EmojiPicker({ 
    emojiable_selector: '[data-emojiable=true]',  
    assetsPath: '/img/', popupButtonClasses: 'fa fa-smile-o'
  }); 
  window.emojiPicker.discover(); 
  var loggedInUser = document.getElementById('logged-in'); 
  loggedInUser.innerHTML = "Logged in as: " + getCookie("username"); 
});

保存文件并刷新浏览器。使用有效的凭证登录,然后点击“注销”。确认注销意图后,页面会刷新,并且 cookies 会被清除。此时,所有用户管理按钮都已连接就绪。

2. 在网页上添加日记条目

当使用布局中的第一张卡片时,选择一个表情符号后,点击巨大的“+”按钮,应该能够将其发送到后端。在 JavaScript 函数列表的底部添加以下函数:

function addEmoji() { 
  var date = new Date(); 
  var dateString = date.toISOString(); 
  var emoji = $("#add-new-emoji-field").val(); 
  if (emoji == "") { 
    alert("You must enter an emoji!"); 
    return; 
  } 
  var xhr = new XMLHttpRequest(); 
  xhr.open("POST", "/entries"); 
  xhr.setRequestHeader("Content-Type", "application/json"); 
  xhr.setRequestHeader("Authorization", "Basic " + btoa(getCookie("username") + ":" + getCookie("password"))); 
  xhr.onreadystatechange = (() => { 
    if (xhr.readyState == XMLHttpRequest.DONE) { 
      if (xhr.status == 201) { 
        location.reload(); 
      } else { 
        alert(`There was an error adding this emoji: ${xhr.statusText}`); 
      } 
    } 
  }); 
  var body = JSON.stringify({ "emoji": emoji, "date": dateString }); 
  xhr.send(body);
}

保存文件并刷新网页浏览器。确保以 socrates 身份登录,尝试添加一个新的表情符号条目。但由于 JavaScript 和 Swift 对日期格式的处理方式不同,可能会遇到问题。

3. 处理日期格式问题

JavaScript 使用 ISO8601 日期格式,而 Kitura API 目前还不能很好地处理这种格式。我们需要使用 Kitura 的自定义解码功能来解决这个问题。
1. 停止服务器,打开 Sources/Application/Routes/EntryRoutes.swift 文件,在导入语句下方添加以下代码:

enum DateError: String, Error { 
  case invalidDate 
} 

let journalEntryDecoder: () -> BodyDecoder = { 
  let decoder = JSONDecoder() 
  decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in 
    let container = try decoder.singleValueContainer() 
    let dateNum = try? container.decode(Double.self) 
    if let dateNum = dateNum, let timeInterval = TimeInterval(exactly: dateNum) { 
      let dateValue = Date(timeIntervalSinceReferenceDate: timeInterval) 
      return dateValue 
    } 
    guard let dateStr = try? container.decode(String.self) else { 
      throw DateError.invalidDate 
    } 
    let formatter = DateFormatter() 
    formatter.calendar = Calendar(identifier: .iso8601) 
    formatter.locale = Locale(identifier: "en_US_POSIX") 
    formatter.timeZone = TimeZone(secondsFromGMT: 0) 
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX" 
    if let date = formatter.date(from: dateStr) { 
      return date 
    } 
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX" 
    if let date = formatter.date(from: dateStr) { 
      return date 
    } 
    throw DateError.invalidDate
  }) 
  return decoder 
}
  1. 将导入 Kitura 的语句更新为:
import KituraContracts
  1. initializeEntryRoutes 函数的底部添加以下代码:
app.router.decoders[.json] = journalEntryDecoder

构建并运行服务器,再次打开网页客户端,尝试添加新的表情符号,应该可以正常显示。

4. 删除网页上的日记条目

每个卡片右上角都有一个大的“X”按钮,现在让它真正起作用。确保服务器正在运行,打开 home.stencil 文件,在函数列表的底部添加以下代码:

function deleteEntry(entryID) { 
  var xhr = new XMLHttpRequest(); 
  xhr.open("DELETE", "/entries/" + entryID); 
  xhr.setRequestHeader("Content-Type", "application/json"); 
  xhr.setRequestHeader("Authorization", "Basic " + btoa(getCookie("username") + ":" + getCookie("password"))); 
  xhr.onreadystatechange = (() => { 
    if (xhr.readyState == XMLHttpRequest.DONE) { 
      location.reload(); 
    } 
  }); 
  xhr.send(); 
}

在 HTML 元素中调用这个函数:

<input id={{ entry.id }} class="delete-button" type="submit" value="&#10005;" onClick="deleteEntry(this.id)" onEntry="hideEmojiPicker()">

保存文件并刷新网页。尝试删除日记条目,你会发现只能删除使用相同登录信息创建的条目。

5. 仅查看自己的日记条目

由于浏览器会在每个请求中包含所有 cookies,我们可以利用这一点来过滤出不需要的条目。打开 WebClientRoutes.swift 文件,在 showClient 路由处理程序中,创建 sortedEntries 的代码行下方添加以下代码:

if let username = request.cookies["username"]?.value, !username.isEmpty { 
  sortedEntries = sortedEntries.filter { $0.user == username } 
}

由于 sortedEntries 之前被声明为常量,需要将其更新为变量:

var sortedEntries = ...

构建并运行服务器,回到网页浏览器。以一个用户身份登录,应该只能看到该用户的条目。以该用户身份添加一个条目,注销并以另一个用户身份登录,也能正常显示对应条目。

6. 使用第三方服务扩展应用

接下来,我们将使用 Fortune Cookie API 为每个日记条目添加一条随机的幸运信息。

6.1 Fortune Cookie API 介绍

Fortune Cookie API 是一个简单的托管 API,可通过 http://yerkee.com/api 访问,支持多种类别,如 all computers cookie 等。可以使用以下 curl 命令进行测试:

curl -X GET "http://yerkee.com/api/fortune/all" -H "accept: application/json"

返回的 JSON 响应包含一个 fortune 字段,例如:

{"fortune":"Indecision is the true basis for flexibility."}
6.2 创建 Fortune 类型

为了使用 Swift 的 Codable 功能解析 JSON 响应,我们需要创建一个对应的 Swift 数据类型。可以使用一些在线工具,如 JSON Cafe 来生成。
1. 打开浏览器访问 http://www.jsoncafe.com ,确保选择 Model Generator 选项卡。
2. 将上述示例 JSON 响应粘贴到 JSON 编辑器窗口中。
3. 设置 Root Class Fortune Code Template Swift Codable
4. 点击 Generate 按钮,将生成的 Fortune.swift 文件添加到项目的 Sources/Application/Models/ 目录中。

6.3 使用 SwiftyRequest 进行 REST 请求

为了从 EmojiJournalServer 调用 Fortune Cookie API ,我们将使用 SwiftyRequest 库。
1. 在 Package.swift 文件的 dependencies 数组中添加以下依赖:

.package(url: "https://github.com/IBM-Swift/SwiftyRequest.git", .upToNextMajor(from: "1.0.0")),
  1. Application 目标的依赖列表中添加 "SwiftyRequest"
.target(name: "Application", dependencies: [ "Kitura", "CloudEnvironment","SwiftMetrics","Health","KituraOpenAPI","SwiftKueryPostgreSQL","SwiftKueryORM","CredentialsHTTP", "KituraStencil", "SwiftyRequest"]),
  1. 打开 Sources/Application/Models/Fortune.swift 文件,添加以下导入语句:
import SwiftyRequest 
import LoggerAPI
  1. 创建 FortuneClient 类:
class FortuneClient { 
  private static var baseURL: String { 
    return "http://yerkee.com" 
  } 

  private static var fortuneURL: String { 
    return "\(baseURL)/api/fortune/all" 
  } 

  public static func getFortune(completion: @escaping (String?) -> Void) { 
    let errorFortune = "No fortune is good fortune" 
    let request = RestRequest(method: .get, url: fortuneURL) 
    request.responseObject() {  
      (response: RestResponse<Fortune>) in 
      switch response.result { 
      case .success(let result): 
        let fortune = result.fortune 
        return completion(fortune) 
      case .failure(let error): 
        Log.error("FortuneClient request failed with \(error)") 
        return completion(errorFortune) 
      } 
    } 
  } 
}
6.4 容错和熔断机制

为了确保应用在远程服务出现问题时仍能正常运行,我们将使用 SwiftyRequest 的熔断机制。
1. 在 Fortune.swift 文件的顶部添加以下导入语句:

import CircuitBreaker
  1. getFortune 函数中,在定义 request 之前配置熔断参数:
let errorFallback = {  
  (error: BreakerError, msg: String) -> Void in 
  Log.error("FortuneClient fallback with \(error)") 
  return completion(errorFortune) 
} 
let circuitParameters = CircuitParameters(timeout: 2000, maxFailures: 2, rollingWindow: 5000, fallback: errorFallback)
  1. 在定义 request 之后,添加以下代码:
request.circuitParameters = circuitParameters

完整的 getFortune 函数如下:

public static func getFortune(completion: @escaping (String?) -> Void) { 
  let errorFortune = "No fortune is good fortune" 
  let errorFallback = {  
    (error: BreakerError, msg: String) -> Void in 
    Log.error("FortuneClient fallback with \(error)") 
    return completion(errorFortune) 
  } 
  let circuitParameters = CircuitParameters(timeout: 2000, maxFailures: 2, rollingWindow: 5000, fallback: errorFallback) 
  let request = RestRequest(method: .get, url: fortuneURL) 
  request.circuitParameters = circuitParameters 
  request.responseObject() {  
    (response: RestResponse<Fortune>) in 
    switch response.result { 
    case .success(let result): 
      let fortune = result.fortune 
      return completion(fortune) 
    case .failure(let error): 
      Log.error("FortuneClient request failed with \(error)") 
      return completion(errorFortune) 
    } 
  } 
}

通过以上步骤,我们不仅实现了 Emoji 日记应用的基本功能,还集成了第三方服务,为应用添加了更多的功能和容错能力。

开发 Emoji 日记应用:功能实现与服务集成

7. 功能总结与流程梳理

为了更好地理解整个开发过程,我们来梳理一下各个功能的实现流程。以下是一个功能实现的流程图:

graph LR
    A[页面加载函数更新] --> B[添加日记条目]
    B --> C[处理日期格式问题]
    C --> D[删除日记条目]
    D --> E[仅查看自己的日记条目]
    E --> F[使用第三方服务扩展应用]
    F --> F1[创建 Fortune 类型]
    F --> F2[使用 SwiftyRequest 进行 REST 请求]
    F --> F3[容错和熔断机制]

下面是各个功能的简要总结表格:
| 功能 | 实现方式 | 关键代码 |
| ---- | ---- | ---- |
| 页面加载函数更新 | 更新页面加载时执行的函数 | javascript $(function() { window.emojiPicker = new EmojiPicker({ emojiable_selector: '[data-emojiable=true]', assetsPath: '/img/', popupButtonClasses: 'fa fa-smile-o' }); window.emojiPicker.discover(); var loggedInUser = document.getElementById('logged-in'); loggedInUser.innerHTML = "Logged in as: " + getCookie("username"); }); |
| 添加日记条目 | 在 JavaScript 中编写函数发送 POST 请求 | javascript function addEmoji() { var date = new Date(); var dateString = date.toISOString(); var emoji = $("#add-new-emoji-field").val(); if (emoji == "") { alert("You must enter an emoji!"); return; } var xhr = new XMLHttpRequest(); xhr.open("POST", "/entries"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader("Authorization", "Basic " + btoa(getCookie("username") + ":" + getCookie("password"))); xhr.onreadystatechange = (() => { if (xhr.readyState == XMLHttpRequest.DONE) { if (xhr.status == 201) { location.reload(); } else { alert(`There was an error adding this emoji: ${xhr.statusText}`); } } }); var body = JSON.stringify({ "emoji": emoji, "date": dateString }); xhr.send(body); } |
| 处理日期格式问题 | 使用 Kitura 的自定义解码功能 | swift enum DateError: String, Error { case invalidDate } let journalEntryDecoder: () -> BodyDecoder = { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in let container = try decoder.singleValueContainer() let dateNum = try? container.decode(Double.self) if let dateNum = dateNum, let timeInterval = TimeInterval(exactly: dateNum) { let dateValue = Date(timeIntervalSinceReferenceDate: timeInterval) return dateValue } guard let dateStr = try? container.decode(String.self) else { throw DateError.invalidDate } let formatter = DateFormatter() formatter.calendar = Calendar(identifier: .iso8601) formatter.locale = Locale(identifier: "en_US_POSIX") formatter.timeZone = TimeZone(secondsFromGMT: 0) formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX" if let date = formatter.date(from: dateStr) { return date } formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX" if let date = formatter.date(from: dateStr) { return date } throw DateError.invalidDate }) return decoder } |
| 删除日记条目 | 在 JavaScript 中编写函数发送 DELETE 请求 | javascript function deleteEntry(entryID) { var xhr = new XMLHttpRequest(); xhr.open("DELETE", "/entries/" + entryID); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader("Authorization", "Basic " + btoa(getCookie("username") + ":" + getCookie("password"))); xhr.onreadystatechange = (() => { if (xhr.readyState == XMLHttpRequest.DONE) { location.reload(); } }); xhr.send(); } |
| 仅查看自己的日记条目 | 在 Swift 中根据 cookie 过滤条目 | swift if let username = request.cookies["username"]?.value, !username.isEmpty { sortedEntries = sortedEntries.filter { $0.user == username } } |
| 使用第三方服务扩展应用 | 创建 Fortune 类型、使用 SwiftyRequest 进行请求、添加容错和熔断机制 | 创建 Fortune 类型:使用 JSON Cafe 工具;使用 SwiftyRequest 进行请求:添加依赖、编写 FortuneClient 类;容错和熔断机制:添加 CircuitBreaker 导入、配置熔断参数 |

8. 实际应用中的注意事项

在实际开发和应用过程中,还有一些需要注意的地方:
- 安全问题
- 目前使用的是基于 cookie 的用户会话管理,建议未来使用基于令牌的身份验证,避免将用户名和密码以明文形式存储在 cookie 中。
- 在处理用户输入时,要进行严格的验证和过滤,防止 SQL 注入、XSS 攻击等安全问题。
- 性能优化
- 对于频繁调用的 API 请求,考虑使用缓存机制,减少不必要的网络请求。
- 对数据库查询进行优化,避免全表扫描等低效操作。
- 兼容性问题
- 不同浏览器对 JavaScript 和 CSS 的支持可能存在差异,需要进行充分的测试,确保应用在各种主流浏览器上都能正常显示和使用。
- 对于 Swift 代码,要注意不同版本的 Swift 编译器和运行时环境的兼容性。

9. 总结

通过以上一系列的操作,我们成功实现了 Emoji 日记应用的多个重要功能,包括页面加载函数更新、日记条目的添加与删除、日期格式处理、用户条目过滤以及使用第三方服务扩展应用等。同时,我们还引入了容错和熔断机制,提高了应用的稳定性和可靠性。

在开发过程中,我们使用了多种技术和工具,如 JavaScript、Swift、jQuery、Kitura、SwiftyRequest 等,充分展示了如何将不同的技术栈结合起来,构建一个功能丰富、性能稳定的 Web 应用。

希望这些内容能为你在开发类似应用时提供一些参考和帮助,你可以根据实际需求对代码和功能进行进一步的扩展和优化。

内容概要:本文档围绕六自由度机械臂的ANN人工神经网络设计展开,涵盖正向逆向运动学求解、正向动力学控制,并采用拉格朗日-欧拉法推导逆向动力学方程,所有内容均通过Matlab代码实现。同时结合RRT路径规划B样条优化技术,提升机械臂运动轨迹的合理性平滑性。文中还涉及多种先进算法仿真技术的应用,如状态估计中的UKF、AUKF、EKF等滤波方法,以及PINN、INN、CNN-LSTM等神经网络模型在工程问题中的建模求解,展示了Matlab在机器人控制、智能算法系统仿真中的强大能力。; 适合人群:具备一定Ma六自由度机械臂ANN人工神经网络设计:正向逆向运动学求解、正向动力学控制、拉格朗日-欧拉法推导逆向动力学方程(Matlab代码实现)tlab编程基础,从事机器人控制、自动化、智能制造、人工智能等相关领域的科研人员及研究生;熟悉运动学、动力学建模或对神经网络在控制系统中应用感兴趣的工程技术人员。; 使用场景及目标:①实现六自由度机械臂的精确运动学动力学建模;②利用人工神经网络解决传统解析方法难以处理的非线性控制问题;③结合路径规划轨迹优化提升机械臂作业效率;④掌握基于Matlab的状态估计、数据融合智能算法仿真方法; 阅读建议:建议结合提供的Matlab代码进行实践操作,重点理解运动学建模神经网络控制的设计流程,关注算法实现细节仿真结果分析,同时参考文中提及的多种优化估计方法拓展研究思路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值