完美:服务器端Swift-响应说明

当请求完成其过程并且业务逻辑已完成与数据的处理/交互时,要求调用HTTPResponse的.completed()函数。 调用它将确保所有未决数据都传递到客户端,并且已建立的TCP连接将被关闭,或者在HTTP保持活动的情况下,将读取并处理新请求。

HTTPResponse对象公开的可用方法在将响应返回给调用方时很有用。 请记住,在我们之前的指南“使用表单数据”之一中,我们已经使用了一种方法-.appendBody(string:String)-该方法只是在响应正文中添加普通字符串,并且当.completed( )方法被调用,应用将其返回给调用者。

让我们看一下HTTPResponse公开的最常用的方法…

.addHeader(名称:HTTPResponseHeader.Name,值:字符串)

通常用于添加与我们返回给调用方的内容相匹配的自定义标头。 因为我们正在构建的应该是一个API,所以您的响应将包含一种类型,仅一种类型是application / json或JSON-但这只是您可以返回的标头类型的一个示例,列表确实很长。

  response.addHeader(.contentType,值:“ application / json”) 

例如,您可以设置的另一种标头类型是.contentEncoding,它将告诉调用方您要返回的数据具有的编码类型。

.setBody()

调用时,此方法有几个选项。

  • 您可以使用简单的字符串.setBody(string:String)设置响应的正文,
  • 您可以使用[String:Any]类型的对象进行设置,并使用.setBody(json:[String:Any])将方法将该对象编码为JSON字符串,
  • 您还可以根据需要使用.setBody(bytes:[UInt8])设置原始正文字节。

下一个与setBody一起工作…

.appendBody()

这会将更多数据附加到已设置的正文内容中。 实际上在使用appendBody之前,您实际上不需要调用setBody。

.appendBody在调用时有两个选择……

  • 使用.appendBody(string:String)附加一个简单的字符串,或者
  • 像上面一样,通过调用.appendBody(bytes:[UInt8])追加原始字节

处理API时,另一种有用且最常用的方法是…

。状态

当您成功地将数据返回给呼叫者时,以及必须告诉呼叫者该操作以错误结束时,响应状态起着重要的作用。

如果我们看一下.status的基础代码,我们会注意到它的类型是HTTPResponseStatus,并且进一步研究这个类型,我们将看到大量的响应代码。

不要惊慌,当您每天使用响应代码时,您会几乎完全了解它们。 让我们将500内部服务器错误状态代码设置回我们的呼叫者

  response.status = HTTPResponseStatus.internalServerError 

如果我们查看一个设置了此状态的curl请求,我们将看到

  <HTTP / 1.1 500内部服务器错误 
<内容类型:application / json
<连接:保持活动
<内容长度:0

HTTPResponse主体还有许多其他可用的方法,我鼓励您尝试一下,如果需要更多详细信息,请参阅Perfect的HTTPResponse官方文档。

让我们在我们的应用程序中测试其中一些方法。 还记得我们所期望的路由firstNamelastNameemailAddress参数吗?

当我们第一次为该路由编写逻辑时,我们将返回一个简单的字符串,让调用者知道我们已收到参数-让我们将其更改为返回JSON字符串。

良好做法提示

在创建或重构任何代码段时,始终建议您在着手编写代码之前列出您需要完成的所有任务-您可以在该函数内使用带注释的行,并且可以在每个注释下键入该任务的代码

因此,按照上面的技巧,我们的重构处理程序最初应该看起来像这样

  func addUserHandler(请求:HTTPRequest,_响应:HTTPResponse){ 
 让firstName = request.param(name:“ firstName”,defaultValue:“”) 
让lastName = request.param(name:“ lastName”,defaultValue:“”)
让emailAddress = request.param(name:“ emailAddress”,defaultValue:“”)
 守卫!(名字?.isEmpty)! 其他{ 
response.appendBody(string:“必须输入名字!”)
response.completed()
返回
}
守护!(lastName?.isEmpty)! 其他{
response.appendBody(字符串:“姓氏为必填项!”)
response.completed()
返回
}
  //构建将返回的对象 
//解包并将firstName添加到返回的对象
//展开并添加lastName到返回的对象中
//将emailAddress添加到对象(如果已设置)
//将响应内容类型设置为application / json
//将响应状态代码设置为200 OK
//使用上述对象设置响应主体
//完成回应
response.completed()
}

出于安全原因,我在此处添加了拆封任务,从一开始就安全起来总比后来才意识到某个地方存在致命错误要好。

还要注意,我们已从emailAddress的request.param行中删除了强制展开!

挑战

在查看下面的完成的代码之前,请尝试自行编写任务的代码,看看是否正确……

  func addUserHandler(请求:HTTPRequest,_响应:HTTPResponse){ 
 让firstName = request.param(name:“ firstName”,defaultValue:“”) 
让lastName = request.param(name:“ lastName”,defaultValue:“”)
让emailAddress = request.param(name:“ emailAddress”,defaultValue:“”)
 守卫!(名字?.isEmpty)! 其他{ 
response.appendBody(string:“必须输入名字!”)
response.completed()
返回
}
守护!(lastName?.isEmpty)! 其他{
response.appendBody(字符串:“姓氏为必填项!”)
response.completed()
返回
}
  //构建将返回的对象 
var userObject:[String:Any] = [:]
  //解包并将firstName添加到返回的对象 
如果让userFirstName = firstName {
userObject [“ firstName”] = userFirstName
}
  //展开并添加lastName到返回的对象中 
如果让userLastName = lastName {
userObject [“ lastName”] = userLastName
}
  //将emailAddress添加到对象(如果已设置) 
如果让userEmailAddress = emailAddress {
userObject [“ emailAddress”] = userEmailAddress
}
  //将响应内容类型设置为application / json 
response.setHeader(.contentType,值:“ application / json”)
  //将响应状态代码设置为200 OK 
response.status = HTTPResponseStatus.ok
  //使用上述对象设置响应主体 
做{
尝试response.setBody(json:userObject)
} {
response.status = HTTPResponseStatus.internalServerError
response.appendBody(string:“ {\”错误\“:\”无法设置正文!\“}”)
}
  //完成回应 
response.completed()
}

好的,让我们逐个任务看这里发生了什么

  • 构建将返回的对象 —我们已经将类型为[String:Any]的变量userObject初始化为一个空字典。 为什么是这种类型? 如果检查setBody的类型,您会注意到它完全接受此类型。
  • 取消包装, 然后将firstName添加到返回的对象中 —为小心起见,不要在我们的对象中添加无效的类型,我们在此处使用了if let语法来取消包装可选变量。
  • 展开并添加lastName到返回的对象中 -相同的逻辑也适用于lastName。
  • 如果已设置则将emailAddress添加到对象 -因为我们从设置此变量的行中删除了强制展开,所以我们在这里被“强制”使用相同的if逻辑进行操作。
  • 将响应内容类型设置为application / json-我会说很简单……我们使用.setHeader方法和application / json值告诉调用者我们返回的是JSON字符串,而不是普通文本。
  • 将响应状态代码设置为200 OK-您可能想知道为什么我们现在还没有将其设置为200,这是因为200是默认的返回状态代码,但是如果您自己设置,这仍然是最佳实践-其他开发人员阅读您的代码将立即看到并理解它。
  • 使用上面的对象设置响应主体 -这是有点棘手的地方。 .setBody方法带有throws标记,这意味着在尝试覆盖/设置响应正文时可能会引发错误。 在这种情况下,我们需要使用do {} catch {}块,在do块内,我们告诉Swift尝试设置主体。 如果一切成功,则执行将继续,但是如果引发错误,则将执行catch块。 在catch块中,我已将响应的状态码更改为500(以表示错误),还附加了一个手动写入的JSON字符串作为错误。 手动JSON字符串不是一个很好的实践,但是我们还没有涉及JSON主题,我们会的。

现在,剩下的就是测试该代码,看看它是否有效。

  curl -v -X POST -F'firstName = Robert'-F'lastName = Bojor'-F'emailAddress=robert@email.com'http:// localhost:8181 /用户 

我添加了详细标志,以便我们可以看到返回数据的标题…

  *正在尝试127.0.0.1 ... 
*连接到localhost(127.0.0.1)端口8181(#0)
> POST /用户HTTP / 1.1
>主机:localhost:8181
>用户代理:curl / 7.43.0
>接受:* / *
>内容长度:369
>期望:100继续
>内容类型:multipart / form-data; 边界= ------------------------ 5b955ecd94f0041f
>
*等待100次继续
<HTTP / 1.1 200确定
<内容类型:application / json
<连接:保持活动
<内容长度:75
<
*与主机本地主机的连接#0保持不变
{“ lastName”:“ Bojor”,“ emailAddress”:“ robert@email.com”,“ firstName”:“ Robert”}

如果检查输出,以<开头的行,我们将看到200的响应代码,将Content-Type设置为application / json,最后一行包含JSON字符串。

成功!

现在,您可以根据自己的需要/需要自定义应用程序的响应对象,并且可以返回您可能需要的任何类型的数据。

在我们的下一个指南中,我们将研究JSON ,如何创建它,如何使用它,因此您不必手动构建错误字符串。

玩得开心,继续编码,
罗伯特·B