问题
I have a Rails app with a Business
model object with a count of visits
. I have an iOS client that interacts with this Rails app using AFNetworking 2.0, and when a user taps a business in the app, it sends a PATCH to the Rails app, incrementing visits
for that business. However, I'm getting a status code 422 "Unprocessable Entity" error back from the server, and the business's visits
count on the server is not getting incremented.
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;
}
Businesses Controller - 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:
If the web API allows to increment a model objects's counter, it should have a dedicated "method" for that.
Generally, an Increment Counter feature won't work reliable if the client itself increments the counter performed on a local copy which it received before from the server and then tries to update the server's value via composing a PATCH request where the client sends just the value local_counter+1
. In the meantime, other clients could have the same idea, in which case the counter value on the server would be become corrupt. Also, one could update the counter on the server to any value - which is certainly not desired.
This has the consequence, that the counter variable MUST be read only for the clients.
There must be other means (defined by the web API) where a client can accomplish this. For example, a REST API would define a particular "resource" (e.g. some sort of "ticket" or "reservation") which a client can create. The creation of this resource will have the effect of incrementing the counter variable on the server.
Edit
If you are testing a web API, the command line tool curl
is invaluable:
Test the PATCH request:
In Terminal.app enter this command:
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
回答2:
The correct solution for this was found on the Rails API site. Here is the code:
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
来源:https://stackoverflow.com/questions/21303258/patch-to-rails-app-from-ios-client-returning-status-422