Correct way to use AFHTTPSessionManager as a singleton? - afnetworking-2

I'm trying to use AFNetworking 2.0 to perform my network requests but I'm running into some odd behavior. I've subclassed AFHTTPSessionManager as suggested in the documentation and provided a class method that returns a singleton object that has the base url set as well as sets my auth header.
+ (id)sharedInstance {
static dispatch_once_t once;
static MyHTTPClient *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] initWithBaseURL: NSURLURLWithString:kPlatformAPIBaseURL]];
});
//Uncommenting this line makes the error go away
//sharedInstance.responseSerializer = [AFJSONResponseSerializer serializer];
//get latest session id everytime someone gets an instance of the client
sharedInstance.sessionId = [MySessionManager getSessionId];
return sharedInstance;
}
- (instancetype)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if(self) {
self.sessionId = [FSSessionManager getSessionId];
self.serializer = [AFHTTPRequestSerializer serializer];
[_serializer setValue:_sessionId forHTTPHeaderField:kAuthorizationHeader];
[_serializer setValue:#"application/json" forHTTPHeaderField:kAcceptHeader];
self.requestSerializer = _serializer;
}
return self;
}
- (void)setSessionId:(NSString *)sessionId {
_sessionId = sessionId;
[self.serializer setValue:_sessionId forHTTPHeaderField:kAuthorizationHeader];
}
My app uses this to make a POST request to authenticate my user. That works great. I then make a GET request to retrieve a list of objects. Also works great. I then make the same GET request and I get back a network error Error Domain=AFNetworkingErrorDomain Code=-1016 "Request failed: unacceptable content-type: application/json" It's the exact same GET request but it fails on the second call. When I uncomment the sharedInstance.responseSerializer line so I create a new instance of the response serializer each time I get a reference to my shared instance then I don't get this error anymore.
Can a responseSerializer not be used multiple times safely? It feels like some sort of state is hanging around across requests. What's the correct way to set this up?

A response serializer can be used multiple times safely. Based on the error message you posted, "unacceptable content-type: application/json ", it appears you're setting responseSerializer to something else elsewhere in your code. JSON will serialize properly as long as it's set to [AFJSONResponseSerializer serializer].

Related

InDesign Socket HTTP response is missing sections

I am automating Adobe InDesign to create documents using JSON data gathered from a web API with a SQL Server backend. I am using the Sockets object to make an HTTP 1.0 call to our server. Sometimes the response received is missing about 1700 characters from various points within the JSON string, yet when I call the same API endpoint using curl or Postman I get a complete and valid response.
The response should be about 150k characters long, and I'm using conn.read(99999999) to read it. In addition, the appearance of the end of the string looks correct, so I don't believe it's any kind of truncation problem.
The problem only seems to occur when I request a UTF-8 encoding. If I request ASCII I get a complete and valid response, but missing various Unicode characters. If I request BINARY I get a complete and valid response but the JavaScript/ExtendScript seems to be handling any multi-byte Unicode characters received as individual bytes, rather than as the Unicode characters we want to display.
Here is an illustration of the behavior I'm seeing, using bogus data...
"Expected" response...
[{"Id":1, "name":"Random Name", "Text":"A bunch of text", "AnotherId": 1}]
"Actual" response...
[{"Id":1, "name":"Random Name", "Text":"A bunc": 1}]
The problem first manifested itself as a JSON2 parsing error, for obvious reasons, but the root of it seems to be the fact that parts of the data are going missing in-transit.
So far we've only seen this problem when making the call using the InDesign Sockets object, and not every response exhibits this behavior.
Any help or insights you could offer would be appreciated.
Here is the function I'm using to call for data...
function httpRequest(url, encoding) {
try {
var response = "";
var hostName = getHostFromUrl(url);
var pathAndQuery = getPathAndQueryFromUrl(url);
var httpGet = "GET ";
httpGet += pathAndQuery;
httpGet += " HTTP/1.0\r\nHost: ";
httpGet += hostName;
httpGet += "\r\n";
var conn = new Socket;
conn.timeout = 30;
//conn.encoding = encoding || "UTF-8";
//conn.charset = "UTF-16";
if (conn.open(hostName + ":80", encoding || "UTF-8")) {
// send a HTTP GET request
conn.writeln(httpGet);
// and read the server's response
response = conn.read(99999999);
conn.close();
}
return parseHttpResponse(response);
}
catch (e) {
$.writeln(e);
$.global.alert("There was a problem making an HTTP Request: " + e);
return null;
}
}
It turns out my handling of the HTTP response was too simplistic and needed extra logic to handle Unicode characters properly.
The solution, in my case, was to use the GetURL method made available by Kris Coppieter here.

HMRC MTD Hello World with Qt

I have a Qt program that stores all my small (tiny) company information on a sql database and I have over the years tailored it to do all my accounting stuff, invoices, BOMs etc.
At the push of a button I can get all of the necessary sql data to calculate a quarterly VAT return, but we're going to have to electronically submit all the data now, not just calculate it. I have all the data needed, it's just a case of submitting over HTTP using json (of which I know a little/nothing about respectively).
I'm small enough so that I don't have to do this submission at the moment, but the time will likely come, so I'm trying the most basic of requests in the HMRC's sandbox as a starting point.
On this page it shows you how to do an hello world request in Java, so I'm trying to do the same with Qt with C++.
I've tried the following which responds to the push of a button and I have of course, set up a slot to deal with a response:
void MainWindow::hello()
{
QJsonObject json;
QString rs("https://test-api.service.hmrc.gov.uk/hello/world");
QNetworkRequest request
{
QUrl(rs)
};
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/vnd.hmrc.1.0+json");
request.setUrl(QUrl(rs));
manager->get(request);
}
and the main window init:
manager = new QNetworkAccessManager();
QObject::connect
(manager, &QNetworkAccessManager::finished, this, [=](QNetworkReply *reply)
{
if (reply->error())
{
ui->debugText->appendHtml(reply->errorString());
return;
}
QString answer = reply->readAll();
ui->debugText->appendHtml(answer);
}
);
To which I get the reply:
Error transferring https://test-api.service.hmrc.gov.uk/hello/world -
server replied: Not Acceptable
I assume that means I am communicating with the sever now, but I do not know what this terse error message means!
The Java on the HMRC web page is as follows:
// construct the GET request for our Hello World endpoint
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(
"https://test-api.service.hmrc.gov.uk/hello/world");
request.addHeader("Accept", "application/vnd.hmrc.1.0+json");
// execute the request
HttpResponse response = client.execute(request);
// extract the HTTP status code and response body
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());
Is that enough information for someone to point me in the right direction of what I'm doing wrong please? Suspect I am missing a fundamental point here.
In your Java example, you are setting the HTTP header "Accept". In your C++/Qt snippet, your are setting the "Content-Type" header.
You may want to adapt your code like this to match your Java working example:
QNetworkRequest request { QUrl(rs) };
request.setRawHeader(QByteArray("Accept"), QByteArray("application/vnd.hmrc.1.0+json"));
manager->get(request);

PUT requests with Custom Ember-Data REST Adapter

I'm using Ember-Data 1.0.0.Beta-9 and Ember 1.7 to consume a REST API via DreamFactory's REST Platform. (http://www.dreamfactory.com).
I've had to extend the RESTAdapter in order to use DF and I've been able to implement GET and POST requests with no problems. I am now trying to implement model.save() (PUT) requests and am having a serious hiccup.
Calling model.save() sends the PUT request with the correct data to my API endpoint and I get a 200 OK response with a JSON response of { "id": "1" } which is what is supposed to happen. However when I try to access the updated record all of the properties are empty except for ID and the record on the server is not updated. I can take the same JSON string passed in the request, paste it into the DreamFactory Swagger API Docs and it works no problem - response is good and the record is updated on the DB.
I've created a JSBin to show all of the code at http://emberjs.jsbin.com/nagoga/1/edit
Unfortunately I can't have a live example as the servers in question are locked down to only accept requests from our company's public IP range.
DreamFactory provides a live demo of the API in question at
https://dsp-sandman1.cloud.dreamfactory.com/swagger/#!/db/replaceRecordsByIds
OK in the end I discovered that you can customize the DreamFactory response by adding a ?fields=* param to the end of the PUT request. I monkey-patched that into my updateRecord method using the following:
updateRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record);
var adapter = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
// hack to make DSP send back the full object
adapter.ajax(adapter.buildURL(type.typeKey) + '?fields=*', "PUT", { data: data }).then(function(json){
// if the request is a success we'll return the same data we passed in
resolve(json);
}, function(reason){
reject(reason.responseJSON);
});
});
}
And poof we haz updates!
DreamFactory has support for tacking several params onto the end of the requests to fully customize the response - at some point I will look to implement this correctly but for the time being I can move forward with my project. Yay!
EmberData is interpreting the response from the server as an empty object with an id of "1" an no other properties in it. You need to return the entire new object back from the server with the changes reflected.

IOS - How to handle return calls from a RESTful service API

I am using an API to have users create an account within my app.
Now I am able to generate the URL required in objective-C to submit the values and in the API documentation it has the return numbers that will confirm to me what has happened.
My question is how do I relay that information to the user of the app?
The return call is shown to me in a HTML page as plain text.
Any ideas?
Thanks in advance.
///////
2012-10-03 12:24:31.557 Multi Web[72631:15203] Dictionary list - {
Connection = "keep-alive";
"Content-Encoding" = gzip;
"Content-Length" = 26;
"Content-Location" = "signup.php";
"Content-Type" = "text/html";
Date = "Tue, 02 Oct 2012 23:24:32 GMT";
P3P = "policyref=\"/w3c/p3p.xml\", CP=\"ALL CURa ADMa DEVa OUR IND UNI COM NAV INT STA PRE\"";
Server = "Apache/2.2.14 (Ubuntu)";
Status = "200 OK";
TCN = choice;
Vary = "negotiate,Accept-Encoding";
"X-Limit-Key-Limit" = 10000;
"X-Limit-Key-Remaining" = 9992;
"X-Limit-Key-Reset" = 236;
"X-Limit-User-Limit" = 320;
"X-Limit-User-Remaining" = 319;
"X-Limit-User-Reset" = 3600;
"X-Powered-By" = "PHP/5.3.2-1ubuntu4.14";
I got this in my console so I now, I have created the account succesfully.
In the middle it says Status = "200 OK";
How do I use that particular line? If I can hook up a UIAlertView to that then i am where I want to be.
Cheers.
I'm not sure if your situation is related to this question. According to docs from the getPocket API you are using, i see the following:
According to apple docs, the default HTTP method is GET. What you need to do is check the response headers from the API. So what you need to do is change your httpMethod to HEAD like so:
NSMutableURLRequest *modReq = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"www.somesite.com/?something"]];
[modReq setHTTPMethod:#"HEAD"];
Then you can read the values from the header with something like so:
NSURLResponse* response = // the response, from somewhere
NSDictionary* headers = [(NSHTTPURLResponse *)response allHeaderFields];
You can then get the response values, and tell the user whats up accordingly.
It's called designing and creating a user interface.
You send a request to the server and get a response. Your job is to examine the response, recognise what it means, and tell the user in an appropriate way what the response meant. Since the user is not an expert in parsing html, showing the html would almost always be entirely inappropriate.
For a user entering a username and password correctly, the appropriate response is usually that the user can now access the site.

Posting a File and Associated Data to a RESTful WebService preferably as JSON

In an application I am developing RESTful API and we want the client to send data as JSON. Part of this application requires the client to upload a file (usually an image) as well as information about the image.
I'm having a hard time tracking down how this happens in a single request. Is it possible to Base64 the file data into a JSON string? Am I going to need to perform 2 posts to the server? Should I not be using JSON for this?
As a side note, we're using Grails on the backend and these services are accessed by native mobile clients (iPhone, Android, etc), if any of that makes a difference.
I asked a similar question here:
How do I upload a file with metadata using a REST web service?
You basically have three choices:
Base64 encode the file, at the expense of increasing the data size by around 33%, and add processing overhead in both the server and the client for encoding/decoding.
Send the file first in a multipart/form-data POST, and return an ID to the client. The client then sends the metadata with the ID, and the server re-associates the file and the metadata.
Send the metadata first, and return an ID to the client. The client then sends the file with the ID, and the server re-associates the file and the metadata.
You can send the file and data over in one request using the multipart/form-data content type:
In many applications, it is possible for a user to be presented with
a form. The user will fill out the form, including information that
is typed, generated by user input, or included from files that the
user has selected. When the form is filled out, the data from the
form is sent from the user to the receiving application.
The definition of MultiPart/Form-Data is derived from one of those
applications...
From http://www.faqs.org/rfcs/rfc2388.html:
"multipart/form-data" contains a series of parts. Each part is
expected to contain a content-disposition header [RFC 2183] where the
disposition type is "form-data", and where the disposition contains
an (additional) parameter of "name", where the value of that
parameter is the original field name in the form. For example, a part
might contain a header:
Content-Disposition: form-data; name="user"
with the value corresponding to the entry of the "user" field.
You can include file information or field information within each section between boundaries. I've successfully implemented a RESTful service that required the user to submit both data and a form, and multipart/form-data worked perfectly. The service was built using Java/Spring, and the client was using C#, so unfortunately I don't have any Grails examples to give you concerning how to set up the service. You don't need to use JSON in this case since each "form-data" section provides you a place to specify the name of the parameter and its value.
The good thing about using multipart/form-data is that you're using HTTP-defined headers, so you're sticking with the REST philosophy of using existing HTTP tools to create your service.
I know that this thread is quite old, however, I am missing here one option. If you have metadata (in any format) that you want to send along with the data to upload, you can make a single multipart/related request.
The Multipart/Related media type is intended for compound objects consisting of several inter-related body parts.
You can check RFC 2387 specification for more in-depth details.
Basically each part of such a request can have content with different type and all parts are somehow related (e.g. an image and it metadata). The parts are identified by a boundary string, and the final boundary string is followed by two hyphens.
Example:
POST /upload HTTP/1.1
Host: www.hostname.com
Content-Type: multipart/related; boundary=xyz
Content-Length: [actual-content-length]
--xyz
Content-Type: application/json; charset=UTF-8
{
"name": "Sample image",
"desc": "...",
...
}
--xyz
Content-Type: image/jpeg
[image data]
[image data]
[image data]
...
--foo_bar_baz--
Here is my approach API (i use example) - as you can see, you I don't use any file_id (uploaded file identifier to the server) in API:
Create photo object on server:
POST: /projects/{project_id}/photos
body: { name: "some_schema.jpg", comment: "blah"}
response: photo_id
Upload file (note that file is in singular form because it is only one per photo):
POST: /projects/{project_id}/photos/{photo_id}/file
body: file to upload
response: -
And then for instance:
Read photos list
GET: /projects/{project_id}/photos
response: [ photo, photo, photo, ... ] (array of objects)
Read some photo details
GET: /projects/{project_id}/photos/{photo_id}
response: { id: 666, name: 'some_schema.jpg', comment:'blah'} (photo object)
Read photo file
GET: /projects/{project_id}/photos/{photo_id}/file
response: file content
So the conclusion is that, first you create an object (photo) by POST, and then you send second request with the file (again POST). To not have problems with CACHE in this approach we assume that we can only delete old photos and add new - no update binary photo files (because new binary file is in fact... NEW photo). However if you need to be able to update binary files and cache them, then in point 4 return also fileId and change 5 to GET: /projects/{project_id}/photos/{photo_id}/files/{fileId}.
I know this question is old, but in the last days I had searched whole web to solution this same question. I have grails REST webservices and iPhone Client that send pictures, title and description.
I don't know if my approach is the best, but is so easy and simple.
I take a picture using the UIImagePickerController and send to server the NSData using the header tags of request to send the picture's data.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"myServerAddress"]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:UIImageJPEGRepresentation(picture, 0.5)];
[request setValue:#"image/jpeg" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"myPhotoTitle" forHTTPHeaderField:#"Photo-Title"];
[request setValue:#"myPhotoDescription" forHTTPHeaderField:#"Photo-Description"];
NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
At the server side, I receive the photo using the code:
InputStream is = request.inputStream
def receivedPhotoFile = (IOUtils.toByteArray(is))
def photo = new Photo()
photo.photoFile = receivedPhotoFile //photoFile is a transient attribute
photo.title = request.getHeader("Photo-Title")
photo.description = request.getHeader("Photo-Description")
photo.imageURL = "temp"
if (photo.save()) {
File saveLocation = grailsAttributes.getApplicationContext().getResource(File.separator + "images").getFile()
saveLocation.mkdirs()
File tempFile = File.createTempFile("photo", ".jpg", saveLocation)
photo.imageURL = saveLocation.getName() + "/" + tempFile.getName()
tempFile.append(photo.photoFile);
} else {
println("Error")
}
I don't know if I have problems in future, but now is working fine in production environment.
FormData Objects: Upload Files Using Ajax
XMLHttpRequest Level 2 adds support for the new FormData interface.
FormData objects provide a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest send() method.
function AjaxFileUpload() {
var file = document.getElementById("files");
//var file = fileInput;
var fd = new FormData();
fd.append("imageFileData", file);
var xhr = new XMLHttpRequest();
xhr.open("POST", '/ws/fileUpload.do');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
alert('success');
}
else if (uploadResult == 'success')
alert('error');
};
xhr.send(fd);
}
https://developer.mozilla.org/en-US/docs/Web/API/FormData
Since the only missing example is the ANDROID example, I'll add it.
This technique uses a custom AsyncTask that should be declared inside your Activity class.
private class UploadFile extends AsyncTask<Void, Integer, String> {
#Override
protected void onPreExecute() {
// set a status bar or show a dialog to the user here
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Integer... progress) {
// progress[0] is the current status (e.g. 10%)
// here you can update the user interface with the current status
}
#Override
protected String doInBackground(Void... params) {
return uploadFile();
}
private String uploadFile() {
String responseString = null;
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://example.com/upload-file");
try {
AndroidMultiPartEntity ampEntity = new AndroidMultiPartEntity(
new ProgressListener() {
#Override
public void transferred(long num) {
// this trigger the progressUpdate event
publishProgress((int) ((num / (float) totalSize) * 100));
}
});
File myFile = new File("/my/image/path/example.jpg");
ampEntity.addPart("fileFieldName", new FileBody(myFile));
totalSize = ampEntity.getContentLength();
httpPost.setEntity(ampEntity);
// Making server call
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode == 200) {
responseString = EntityUtils.toString(httpEntity);
} else {
responseString = "Error, http status: "
+ statusCode;
}
} catch (Exception e) {
responseString = e.getMessage();
}
return responseString;
}
#Override
protected void onPostExecute(String result) {
// if you want update the user interface with upload result
super.onPostExecute(result);
}
}
So, when you want to upload your file just call:
new UploadFile().execute();
I wanted send some strings to backend server. I didnt use json with multipart, I have used request params.
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public void uploadFile(HttpServletRequest request,
HttpServletResponse response, #RequestParam("uuid") String uuid,
#RequestParam("type") DocType type,
#RequestParam("file") MultipartFile uploadfile)
Url would look like
http://localhost:8080/file/upload?uuid=46f073d0&type=PASSPORT
I am passing two params (uuid and type) along with file upload.
Hope this will help who don't have the complex json data to send.
You could try using https://square.github.io/okhttp/ library.
You can set the request body to multipart and then add the file and json objects separately like so:
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("uploadFile", uploadFile.getName(), okhttp3.RequestBody.create(uploadFile, MediaType.parse("image/png")))
.addFormDataPart("file metadata", json)
.build();
Request request = new Request.Builder()
.url("https://uploadurl.com/uploadFile")
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
logger.info(response.body().string());
#RequestMapping(value = "/uploadImageJson", method = RequestMethod.POST)
public #ResponseBody Object jsongStrImage(#RequestParam(value="image") MultipartFile image, #RequestParam String jsonStr) {
-- use com.fasterxml.jackson.databind.ObjectMapper convert Json String to Object
}
Please ensure that you have following import. Ofcourse other standard imports
import org.springframework.core.io.FileSystemResource
void uploadzipFiles(String token) {
RestBuilder rest = new RestBuilder(connectTimeout:10000, readTimeout:20000)
def zipFile = new File("testdata.zip")
def Id = "001G00000"
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>()
form.add("id", id)
form.add('file',new FileSystemResource(zipFile))
def urld ='''http://URL''';
def resp = rest.post(urld) {
header('X-Auth-Token', clientSecret)
contentType "multipart/form-data"
body(form)
}
println "resp::"+resp
println "resp::"+resp.text
println "resp::"+resp.headers
println "resp::"+resp.body
println "resp::"+resp.status
}