The "documentation about request based applications in general" only gives a high-level overview about what is possible. This chapter is intended to be read by developers who like to know more about request based applications.
Here is a list of the most important facts about request based applications:
Your should already know how to add routes and handlers to your cloud application. If you don't know how to do that please read the documentation about request based applications.
When adding a request handler you have to specify a HTTP method and a path like this:
1 #import "CloudApp.h"
2 #import <OCFoundation/OCFoundation.h>
3
4 @implementation CloudApp
5
6 + (void)finishLaunching {
7 NSBundle *mainBundle = NSBundle.mainBundle;
8 // Add the route and handler
9 [self handleRequestsWithMethod:@"GET"
10 matchingPath:@"/"
11 withBlock:^(OCFRequest *request) {
12 // respond with "Hello World"
13 [request respondWith:@"Hello World"];
14 }];
15 }
16
17 @end
In the examples above we used GET
as the HTTP method and /
as the path. You can do more sophisticated things though.
The HTTP method you specify can be a regular expression. For example: This allows you to route all requests - no matter the request method - to a single handler (by using @"^.+$"
) as the method. It should be mentioned that doing this is considered bad practice. If you have the exact same handler for different HTTP methods you should rethink your architecture.
The path can be a pattern with placeholders. A placeholder begins with a :
. This is useful if you have hierarchical URLs/paths like this:
GET /countries/
: Lists all countries.GET /countries/Germany/
: Lists only the country called Germany.GET /countries/Germany/states/Berlin/
: Lists only the german state Berlin.Let's assume you want to add and implement a handler that displays a specific state. You would do that by using the path pattern /countries/:country/states/:state/
.
This assumes that the path component of your cloud application is set to
countries
.
The following example shows you how to register a handler that is only executed if the request path is matching a specific pattern.
1 #import "CloudApp.h"
2 #import <OCFoundation/OCFoundation.h>
3
4 @implementation CloudApp
5
6 + (void)finishLaunching {
7 [self handleRequestsWithMethod:@"GET"
8 matchingPath:@"/countries/:country/states/:state/"
9 withBlock:^(OCFRequest *request) {
10 // respond with "Hello World"
11 [request respondWith:[request.parameters description]];
12 }];
13 }
14
15 @end
The pattern used (/countries/:country/states/:state/
) has two placeholders:
If a request with a matching path comes in (GET /countries/Germany/states/Berlin
) then the specified handler is executed. The parameters
property of the request passed to the handler is a dictionary which contains two key/value-pairs:
country = Germany
state = Berlin
The request handler simply returns a description of the dictionary in this case. So you don't have to parse the path of the request yourself.
There is no limit regarding the number of handlers you add. If a request comes in then one out of many handlers has to be picked and executed. OCFWebApplication finds a handler for an incoming request in two steps:
Multipart requests are often used to allow users to upload files by using an HTML form like this:
1 <form method="POST" action="/upload" id="files" enctype="multipart/form-data">
2 <input name="file-a" type="file" size="50" maxlength="1048576" accept="*"/>
3 <input name="file-b" type="file" size="50" maxlength="1048576" accept="*"/>
4 <input type="submit" value="Upload">
5 </form>
This form allows someone to upload two files (file-a
and file-b
). When a multipart request reaches one of your handlers you can get the uploaded files by using the request object. This is where the parameters of a request come in again. The parts of a multipart request are simply entries in the parameters dictionary. With regards to the example above you would access the multipart information like this:
1 [self handleRequestsWithMethod:@"POST"
2 matchingPath:@"/upload"
3 withBlock:^(OCFRequest *request) {
4 NSDictionary *parameters = request.parameters;
5
6 // file-a
7 NSDictionary *fileA = parameters[@"file-a"];
8 NSString *temporaryPathA = fileA[@"temporaryPath"];
9 NSString *contentTypeA = fileA[@"contentType"];
10 NSString *filenameA = fileA[@"filename"];
11 [self doSomethingWithFile:temporaryPathA];
12
13 // file-b
14 NSDictionary *fileB = parameters[@"file-b"];
15 NSString *temporaryPathB = fileB[@"temporaryPath"];
16 NSString *contentTypeB = fileB[@"contentType"];
17 NSString *filenameB = fileB[@"filename"];
18 [self doSomethingWithFile:temporaryPathB];
19
20 // Send something back to the client
21 [request respondWith:@"Got it!";
22
23 // Do not access temporaryPathA and temporaryPathB
24 // after you called -respondWith:.
25 }];
Each part is represented by a dictionary with three key-value-pairs:
temporaryPath
(mandatory): An NSString pointing to a temporary file which contains the raw data of the part. You can access the file by loading it in an NSData object or some other means. The file at temporaryPath
will automatically be deleted once you call -respondWith:
. contentType
(optional): An NSString which contains the content type of the file. You can use the UTI API from Apple to convert the content type to an actual UTI if you need to. filename
(optional): An NSString which contains the name of the uploaded file.Your code should not crash if one or all of the optional key-value-paris are missing.