从 iOS 客户端返回状态 422 到 Rails 应用程序的 PATCH

Posted

技术标签:

【中文标题】从 iOS 客户端返回状态 422 到 Rails 应用程序的 PATCH【英文标题】:PATCH to Rails app from iOS Client Returning Status 422 【发布时间】:2014-01-23 08:44:06 【问题描述】:

我有一个带有 Business 模型对象的 Rails 应用程序,其计数为 visits。我有一个使用 AFNetworking 2.0 与这个 Rails 应用程序交互的 ios 客户端,当用户在应用程序中点击一个业务时,它会向 Rails 应用程序发送一个 PATCH,为该业务增加 visits。但是,我从服务器收到状态代码 422“无法处理的实体”错误,并且服务器上企业的 visits 计数没有增加。

AFHTTPSessionManager - iOS

- (NSURLSessionDataTask *)visitVenue:(NOTVenue *)venue withCompletionBlock:(void (^)(BOOL success))completionBlock 
NSURLSessionDataTask *dataTask = [self PATCH:[NSString stringWithFormat:@"businesses/%li.json", (long)[venue.identifier integerValue]]
                                  parameters:@@"visits": @(venue.numberOfVisits + 1)
                                     success:^(NSURLSessionDataTask *task, id responseObject) 
                                         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
                                         
                                         if (httpResponse.statusCode == 200) 
                                             dispatch_async(dispatch_get_main_queue(), ^
                                                 completionBlock(YES);
                                             );
                                          else 
                                             dispatch_async(dispatch_get_main_queue(), ^
                                                 completionBlock(NO);
                                             );
                                         
                                     
                                     failure:^(NSURLSessionDataTask *task, NSError *error) 
                                         dispatch_async(dispatch_get_main_queue(), ^
                                             completionBlock(NO);
                                         );
                                     ];

return dataTask;

业务控制器 - Rails

# PATCH/PUT /businesses/1
# PATCH/PUT /businesses/1.json
def update
  respond_to do |format|
    if @business.update(business_params)
      format.html  redirect_to @business, notice: 'Business was successfully updated.' 
      format.json  head :no_content 
    else
      format.html  render action: 'edit' 
      format.json  render json: @business.errors, status: :unprocessable_entity 
    end
  end
end

【问题讨论】:

【参考方案1】:

如果 Web API 允许增加模型对象的计数器,它应该有一个专用的“方法”。

一般来说,如果客户端本身递增在它之前从服务器接收到的 本地副本 上执行的计数器然后尝试,递增计数器 功能将无法可靠地工作通过编写 PATCH 请求来更新服务器的值,其中客户端仅发送值 local_counter+1。同时,其他客户端可能有相同的想法,在这种情况下,服务器上的计数器值将损坏。此外,可以将服务器上的计数器更新为 any 值 - 这当然是不希望的。

这会导致计数器变量对于客户端必须只读

客户端必须有其他方式(由 Web API 定义)来完成此操作。例如,REST API 将定义客户端可以创建的特定“资源”(例如某种“票证”或“预订”)。此资源的创建将具有递增服务器上的计数器变量的效果。

编辑

如果您正在测试 Web API,那么命令行工具 curl 是无价的:

测试 PATCH 请求:

在 Terminal.app 中输入以下命令:

curl -sSv -X PATCH \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -d '"email”:”jappleseed@mail.com"' \
    http://localhost:3000/api/v1/users/123 \
    | python -m json.tool

【讨论】:

谢谢,这绝对有帮助;我对客户端服务器设计很陌生。虽然我肯定会改变我的方法,但你能解释一下为什么 PATCH 会返回 422 吗?为什么它不起作用仍然没有意义。 @sethfri 我不知道它为什么会发送 422。如果 Web 服务是由您实现的,您应该知道原因;) 422(不可处理实体)不是 HTTP 1.1 状态码,而是它由 WebDAV (RFC 4918) 定义。我怀疑 Rails 核心会发出该代码。 它就在 Rails 生成的控制器代码中 - :unprocessable_entity。我只是想不通为什么要发送。我的 PATCH 参数看起来是正确的,对吧(如果在这种情况下 PATCH 是正确的方法)? @sethfri :unprocessable_entity 确实映射到状态码 422。也就是说,您的 update 方法失败,原因不明(对我来说)。您可以检查数据模型。 CouchDeveloper: 422 一个HTTP/1.1状态码;它只是没有在 RFC 2616 中定义。【参考方案2】:

在Rails API site 上找到了正确的解决方案。代码如下:

class ApplicationController < ActionController::Base
  protect_from_forgery
  skip_before_action :verify_authenticity_token, if: :json_request?

  protected

  def json_request?
    request.format.json?
  end
end

【讨论】:

以上是关于从 iOS 客户端返回状态 422 到 Rails 应用程序的 PATCH的主要内容,如果未能解决你的问题,请参考以下文章

Rails API 422 无法处理的实体:没有可用的验证密钥,heroku

如何反序列化 422 无法处理的实体错误模型并将错误绑定到 asp.net core razor pages 模型状态

Rails:Rails 中的 POST 422(不可处理实体)?由于路线或控制器?

Angular + Rails:POST 请求 422/CSRF 错误

PayPal Orders v2 在传递金额折扣时创建返回 422(无法处理的实体)

AFnetworking 不断返回响应 Code=-1011 “请求失败:客户端错误 (422)