🎯 Exemples recommandés
Balanced sample collections from various categories for you to explore
Fonctions Web macOS Objective-C - Exemples
Exemples fonctions web macOS Objective-C incluant routage URL, middleware fichiers statiques
💻 Routage URL objectivec
🟡 intermediate
⭐⭐⭐⭐
Traiter routes URL paramètres chemin chaînes requête
⏱️ 35 min
🏷️ objectivec, macos, web, routing
Prerequisites:
Intermediate Objective-C, Foundation framework
// macOS Objective-C URL Routing Examples
// Using Foundation framework
#import <Foundation/Foundation.h>
// MARK: - 1. URL Parser
@interface URLParser : NSObject
+ (NSDictionary *)parseURL:(NSString *)urlString;
+ (NSString *)getPath:(NSString *)urlString;
+ (NSDictionary *)getQueryParameters:(NSString *)urlString;
+ (NSDictionary *)getPathComponents:(NSString *)urlString;
@end
@implementation URLParser
+ (NSDictionary *)parseURL:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
if (!url) {
NSLog(@"Invalid URL: %@", urlString);
return nil;
}
NSMutableDictionary *result = [NSMutableDictionary dictionary];
result[@"scheme"] = url.scheme;
result[@"host"] = url.host;
result[@"port"] = url.port ?: @"default";
result[@"path"] = url.path;
result[@"query"] = url.query;
result[@"fragment"] = url.fragment;
NSLog(@"Parsed URL: %@", result);
return [result copy];
}
+ (NSString *)getPath:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSString *path = url.path;
NSLog(@"Path: %@", path);
return path;
}
+ (NSDictionary *)getQueryParameters:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSString *query = url.query;
if (!query) {
return @{};
}
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
NSArray *pairs = [query componentsSeparatedByString:@"&"];
for (NSString *pair in pairs) {
NSArray *keyValue = [pair componentsSeparatedByString:@"="];
if (keyValue.count == 2) {
NSString *key = keyValue[0];
NSString *value = [keyValue[1] stringByRemovingPercentEncoding];
// Handle multiple values for same key
if (parameters[key]) {
// Convert to array if not already
id existing = parameters[key];
if ([existing isKindOfClass:[NSArray class]]) {
NSMutableArray *array = [existing mutableCopy];
[array addObject:value];
parameters[key] = array;
} else {
parameters[key] = @[existing, value];
}
} else {
parameters[key] = value;
}
}
}
NSLog(@"Query parameters: %@", parameters);
return [parameters copy];
}
+ (NSDictionary *)getPathComponents:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSString *path = url.path;
if (!path) {
return @{};
}
// Remove leading slash and split
NSString *trimmedPath = [path hasPrefix:@"/"] ? [path substringFromIndex:1] : path;
NSArray *components = [trimmedPath componentsSeparatedByString:@"/"];
// Filter out empty components
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self != ''"];
NSArray *filteredComponents = [components filteredArrayUsingPredicate:predicate];
NSMutableDictionary *result = [NSMutableDictionary dictionary];
result[@"components"] = filteredComponents;
result[@"count"] = @(filteredComponents.count);
NSLog(@"Path components: %@", filteredComponents);
return [result copy];
}
@end
// MARK: - 2. Route Definition
@interface Route : NSObject
@property (nonatomic, strong) NSString *pattern;
@property (nonatomic, strong) NSString *method;
@property (nonatomic, copy) void (^handler)(NSDictionary *params, NSDictionary *query);
- (instancetype)initWithPattern:(NSString *)pattern
method:(NSString *)method
handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;
- (BOOL)matchesPath:(NSString *)path
parameters:(NSDictionary **)parameters;
@end
@implementation Route
- (instancetype)initWithPattern:(NSString *)pattern
method:(NSString *)method
handler:(void (^)(NSDictionary *, NSDictionary *))handler {
self = [super init];
if (self) {
_pattern = pattern;
_method = method;
_handler = handler;
}
return self;
}
- (BOOL)matchesPath:(NSString *)path
parameters:(NSDictionary **)parameters {
// Convert pattern to regex
// /users/:id -> /users/([^/]+)
NSString *regexPattern = self.pattern;
// Escape regex special characters except our parameter marker
NSCharacterSet *specialChars = [NSCharacterSet characterSetWithCharactersInString:@".^$*+(){}[]|\\"];
regexPattern = [regexPattern stringByTrimmingCharactersInSet:specialChars];
// Replace :parameter with regex capture group
regexPattern = [self.pattern stringByReplacingOccurrencesOfString:@":([^/]+)"
withString:@"([^/]+)"
options:NSRegularExpressionSearch
range:NSMakeRange(0, self.pattern.length)
error:nil];
// Add anchors
regexPattern = [NSString stringWithFormat:@"^%@$", regexPattern];
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexPattern
options:0
error:&error];
if (error) {
NSLog(@"Invalid route pattern: %@", error.localizedDescription);
return NO;
}
NSTextCheckingResult *match = [regex firstMatchInString:path
options:0
range:NSMakeRange(0, path.length)];
if (match) {
// Extract parameter names from pattern
NSMutableArray *paramNames = [NSMutableArray array];
NSRegularExpression *paramRegex = [NSRegularExpression regularExpressionWithPattern:@":([^/]+)"
options:0
error:nil];
[paramRegex enumerateMatchesInString:self.pattern
options:0
range:NSMakeRange(0, self.pattern.length)
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSString *paramName = [self.pattern substringWithRange:[result rangeAtIndex:1]];
[paramNames addObject:paramName];
}];
// Extract parameter values
NSMutableDictionary *params = [NSMutableDictionary dictionary];
for (NSInteger i = 0; i < paramNames.count && i < match.numberOfRanges - 1; i++) {
NSRange valueRange = [match rangeAtIndex:i + 1];
NSString *value = [path substringWithRange:valueRange];
params[paramNames[i]] = value;
}
*parameters = [params copy];
return YES;
}
return NO;
}
@end
// MARK: - 3. Router
@interface Router : NSObject
@property (nonatomic, strong) NSMutableArray<Route *> *routes;
- (void)addRoute:(Route *)route;
- (BOOL)handleRequest:(NSString *)method
path:(NSString *)path
query:(NSDictionary *)query;
- (void)GET:(NSString *)pattern
handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;
- (void)POST:(NSString *)pattern
handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;
- (void)PUT:(NSString *)pattern
handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;
- (void)DELETE:(NSString *)pattern
handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;
@end
@implementation Router
- (instancetype)init {
self = [super init];
if (self) {
_routes = [NSMutableArray array];
}
return self;
}
- (void)addRoute:(Route *)route {
[self.routes addObject:route];
NSLog(@"Added route: %@ %@", route.method, route.pattern);
}
- (BOOL)handleRequest:(NSString *)method
path:(NSString *)path
query:(NSDictionary *)query {
for (Route *route in self.routes) {
if ([route.method isEqualToString:method]) {
NSDictionary *params = nil;
if ([route matchesPath:path parameters:¶ms]) {
NSLog(@"Route matched: %@ %@", method, route.pattern);
route.handler(params, query);
return YES;
}
}
}
NSLog(@"No route matched: %@ %@", method, path);
return NO;
}
- (void)GET:(NSString *)pattern
handler:(void (^)(NSDictionary *, NSDictionary *))handler {
Route *route = [[Route alloc] initWithPattern:pattern
method:@"GET"
handler:handler];
[self addRoute:route];
}
- (void)POST:(NSString *)pattern
handler:(void (^)(NSDictionary *, NSDictionary *))handler {
Route *route = [[Route alloc] initWithPattern:pattern
method:@"POST"
handler:handler];
[self addRoute:route];
}
- (void)PUT:(NSString *)pattern
handler:(void (^)(NSDictionary *, NSDictionary *))handler {
Route *route = [[Route alloc] initWithPattern:pattern
method:@"PUT"
handler:handler];
[self addRoute:route];
}
- (void)DELETE:(NSString *)pattern
handler:(void (^)(NSDictionary *, NSDictionary *))handler {
Route *route = [[Route alloc] initWithPattern:pattern
method:@"DELETE"
handler:handler];
[self addRoute:route];
}
@end
// MARK: - 4. Request Handler
@interface RequestHandler : NSObject
+ (void)handleRequest:(NSString *)method
path:(NSString *)path
query:(NSDictionary *)query
headers:(NSDictionary *)headers
body:(NSData *)body;
@end
@implementation RequestHandler
+ (void)handleRequest:(NSString *)method
path:(NSString *)path
query:(NSDictionary *)query
headers:(NSDictionary *)headers
body:(NSData *)body {
NSLog(@"\n=== Incoming Request ===");
NSLog(@"Method: %@", method);
NSLog(@"Path: %@", path);
NSLog(@"Query: %@", query);
NSLog(@"Headers: %@", headers);
NSLog(@"Body Length: %lu bytes", (unsigned long)body.length);
// Simulate response
NSDictionary *response = @{
@"status": @"200 OK",
@"headers": @{
@"Content-Type": @"application/json"
},
@"body": @{
@"message": @"Request processed successfully",
@"path": path,
@"method": method
}
};
NSLog(@"Response: %@", response);
}
@end
// MARK: - 5. Complete Routing System
@interface WebServer : NSObject
@property (nonatomic, strong) Router *router;
- (instancetype)init;
- (void)handleRequestString:(NSString *)requestString;
@end
@implementation WebServer
- (instancetype)init {
self = [super init];
if (self) {
_router = [[Router alloc] init];
[self setupRoutes];
}
return self;
}
- (void)setupRoutes {
// Home page
[self.router GET:@"/"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"Handling home page");
NSLog(@"Response: Welcome to the home page!");
}];
// User routes
[self.router GET:@"/users"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"Handling user list");
NSLog(@"Response: List of all users");
}];
[self.router GET:@"/users/:id"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"Handling user detail");
NSLog(@"User ID: %@", params[@"id"]);
NSLog(@"Response: User details for ID: %@", params[@"id"]);
}];
// Create user
[self.router POST:@"/users"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"Handling user creation");
NSLog(@"Response: User created");
}];
// Update user
[self.router PUT:@"/users/:id"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"Handling user update");
NSLog(@"User ID: %@", params[@"id"]);
NSLog(@"Response: User updated");
}];
// Delete user
[self.router DELETE:@"/users/:id"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"Handling user deletion");
NSLog(@"User ID: %@", params[@"id"]);
NSLog(@"Response: User deleted");
}];
// Products with category filter
[self.router GET:@"/products/:category"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"Handling product list by category");
NSLog(@"Category: %@", params[@"category"]);
NSLog(@"Query: %@", query);
NSLog(@"Response: Products in category: %@", params[@"category"]);
}];
}
- (void)handleRequestString:(NSString *)requestString {
// Parse request: "GET /users/123?sort=name HTTP/1.1"
NSArray *components = [requestString componentsSeparatedByString:@" "];
if (components.count < 2) {
NSLog(@"Invalid request: %@", requestString);
return;
}
NSString *method = components[0];
NSString *fullPath = components[1];
// Split path and query
NSArray *pathComponents = [fullPath componentsSeparatedByString:@"?"];
NSString *path = pathComponents[0];
NSDictionary *query = pathComponents.count > 1 ? [URLParser getQueryParameters:fullPath] : @{};
// Handle through router
BOOL handled = [self.router handleRequest:method
path:path
query:query];
if (!handled) {
NSLog(@"404 Not Found: %@", path);
}
}
@end
// MARK: - Main Demonstration
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"=== macOS Objective-C URL Routing Examples ===\n");
// 1. URL parsing
NSLog(@"--- 1. URL Parsing ---");
NSString *testURL1 = @"https://api.example.com/users/123?sort=name&order=asc";
[URLParser parseURL:testURL1];
[URLParser getPath:testURL1];
[URLParser getQueryParameters:testURL1];
[URLParser getPathComponents:testURL1];
// 2. Route matching
NSLog(@"\n--- 2. Route Matching ---");
Route *userRoute = [[Route alloc] initWithPattern:@"/users/:id"
method:@"GET"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"Handler called with params: %@", params);
}];
NSDictionary *params = nil;
BOOL matches = [userRoute matchesPath:@"/users/123" parameters:¶ms];
NSLog(@"Matches /users/123: %@ (params: %@)", matches ? @"YES" : @"NO", params);
// 3. Router setup
NSLog(@"\n--- 3. Router Setup ---");
Router *router = [[Router alloc] init];
[router GET:@"/posts"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"GET /posts - Blog posts list");
}];
[router GET:@"/posts/:id"
handler:^(NSDictionary *params, NSDictionary *query) {
NSLog(@"GET /posts/%@ - Single post", params[@"id"]);
}];
// 4. Request handling
NSLog(@"\n--- 4. Request Handling ---");
[router handleRequest:@"GET"
path:@"/posts"
query:@{@"page": @"1", @"limit": @"10"}];
[router handleRequest:@"GET"
path:@"/posts/42"
query:@{}];
// 5. Complete web server
NSLog(@"\n--- 5. Complete Web Server ---");
WebServer *server = [[WebServer alloc] init];
// Simulate requests
NSLog(@"\nSimulating HTTP requests:");
NSLog(@"\nGET /");
[server handleRequestString:@"GET / HTTP/1.1"];
NSLog(@"\nGET /users");
[server handleRequestString:@"GET /users HTTP/1.1"];
NSLog(@"\nGET /users/123");
[server handleRequestString:@"GET /users/123 HTTP/1.1"];
NSLog(@"\nGET /products/electronics?sort=price");
[server handleRequestString:@"GET /products/electronics?sort=price HTTP/1.1"];
NSLog(@"\nPOST /users");
[server handleRequestString:@"POST /users HTTP/1.1"];
NSLog(@"\nPUT /users/456");
[server handleRequestString:@"PUT /users/456 HTTP/1.1"];
NSLog(@"\nDELETE /users/789");
[server handleRequestString:@"DELETE /users/789 HTTP/1.1"];
NSLog(@"\nGET /nonexistent");
[server handleRequestString:@"GET /nonexistent HTTP/1.1"];
NSLog(@"\n=== URL Routing Examples Completed ===");
}
return 0;
}
💻 Fichiers Statiques objectivec
🟡 intermediate
⭐⭐⭐
Servir fichiers statiques détection MIME support cache
⏱️ 30 min
🏷️ objectivec, macos, web, static files
Prerequisites:
Objective-C basics, Foundation framework
// macOS Objective-C Static Files Examples
// Using Foundation framework
#import <Foundation/Foundation.h>
// MARK: - 1. MIME Type Detector
@interface MIMETypeDetector : NSObject
+ (instancetype)sharedDetector;
+ (NSString *)MIMETypeForExtension:(NSString *)extension;
+ (NSString *)MIMETypeForFile:(NSString *)filePath;
@end
@implementation MIMETypeDetector
+ (instancetype)sharedDetector {
static MIMETypeDetector *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[MIMETypeDetector alloc] init];
});
return instance;
}
+ (NSString *)MIMETypeForExtension:(NSString *)extension {
NSDictionary *mimeTypes = @{
// Text
@"html": @"text/html",
@"htm": @"text/html",
@"css": @"text/css",
@"js": @"application/javascript",
@"json": @"application/json",
@"xml": @"application/xml",
@"txt": @"text/plain",
@"md": @"text/markdown",
// Images
@"png": @"image/png",
@"jpg": @"image/jpeg",
@"jpeg": @"image/jpeg",
@"gif": @"image/gif",
@"bmp": @"image/bmp",
@"svg": @"image/svg+xml",
@"ico": @"image/x-icon",
@"webp": @"image/webp",
// Fonts
@"ttf": @"font/ttf",
@"otf": @"font/otf",
@"woff": @"font/woff",
@"woff2": @"font/woff2",
@"eot": @"application/vnd.ms-fontobject",
// Documents
@"pdf": @"application/pdf",
@"doc": @"application/msword",
@"docx": @"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
@"xls": @"application/vnd.ms-excel",
@"xlsx": @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
// Audio
@"mp3": @"audio/mpeg",
@"wav": @"audio/wav",
@"ogg": @"audio/ogg",
// Video
@"mp4": @"video/mp4",
@"mov": @"video/quicktime",
@"avi": @"video/x-msvideo",
@"webm": @"video/webm",
// Archives
@"zip": @"application/zip",
@"tar": @"application/x-tar",
@"gz": @"application/gzip",
// Default
@"bin": @"application/octet-stream"
};
NSString *lowerExt = [extension lowercaseString];
NSString *mimeType = mimeTypes[lowerExt];
if (!mimeType) {
return @"application/octet-stream";
}
return mimeType;
}
+ (NSString *)MIMETypeForFile:(NSString *)filePath {
NSString *extension = [filePath pathExtension];
return [self MIMETypeForExtension:extension];
}
@end
// MARK: - 2. Static File Handler
@interface StaticFileHandler : NSObject
@property (nonatomic, strong) NSString *documentRoot;
@property (nonatomic, strong) NSString *indexFile;
- (instancetype)initWithDocumentRoot:(NSString *)root;
- (NSData *)serveFileAtPath:(NSString *)path
mimeType:(NSString **)mimeType
statusCode:(NSInteger *)statusCode;
- (NSDictionary *)fileResponseForPath:(NSString *)path;
@end
@implementation StaticFileHandler
- (instancetype)initWithDocumentRoot:(NSString *)root {
self = [super init];
if (self) {
_documentRoot = root;
_indexFile = @"index.html";
}
return self;
}
- (NSData *)serveFileAtPath:(NSString *)path
mimeType:(NSString **)mimeType
statusCode:(NSInteger *)statusCode {
// Remove leading slash
NSString *relativePath = [path hasPrefix:@"/"] ? [path substringFromIndex:1] : path;
// If directory, append index file
if ([relativePath hasSuffix:@"/"] || relativePath.length == 0) {
relativePath = [relativePath stringByAppendingString:self.indexFile];
}
// Build full path
NSString *fullPath = [self.documentRoot stringByAppendingPathComponent:relativePath];
// Security: Check path doesn't escape document root
NSString *canonicalFullPath = [fullPath stringByStandardizingPath];
NSString *canonicalRoot = [self.documentRoot stringByStandardizingPath];
if (![canonicalFullPath hasPrefix:canonicalRoot]) {
NSLog(@"Attempted path traversal attack: %@", path);
*statusCode = 403;
*mimeType = @"text/plain";
return [@"Forbidden: Path traversal detected" dataUsingEncoding:NSUTF8StringEncoding];
}
// Check if file exists
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:fullPath]) {
NSLog(@"File not found: %@", fullPath);
*statusCode = 404;
*mimeType = @"text/plain";
return [@"404 Not Found" dataUsingEncoding:NSUTF8StringEncoding];
}
// Check if it's a directory
BOOL isDirectory = NO;
[fileManager fileExistsAtPath:fullPath isDirectory:&isDirectory];
if (isDirectory) {
// Try index file
fullPath = [fullPath stringByAppendingPathComponent:self.indexFile];
if (![fileManager fileExistsAtPath:fullPath]) {
*statusCode = 404;
*mimeType = @"text/plain";
return [@"404 Not Found" dataUsingEncoding:NSUTF8StringEncoding];
}
}
// Read file
NSError *error = nil;
NSData *fileData = [NSData dataWithContentsOfFile:fullPath options:0 error:&error];
if (error) {
NSLog(@"Error reading file: %@", error.localizedDescription);
*statusCode = 500;
*mimeType = @"text/plain";
return [@"500 Internal Server Error" dataUsingEncoding:NSUTF8StringEncoding];
}
// Determine MIME type
*mimeType = [[MIMETypeDetector sharedDetector] MIMETypeForFile:fullPath];
*statusCode = 200;
NSLog(@"Serving file: %@ (%@)", fullPath, *mimeType);
return fileData;
}
- (NSDictionary *)fileResponseForPath:(NSString *)path {
NSString *mimeType = nil;
NSInteger statusCode = 0;
NSData *body = [self serveFileAtPath:path
mimeType:&mimeType
statusCode:&statusCode];
return @{
@"statusCode": @(statusCode),
@"headers": @{
@"Content-Type": mimeType
},
@"body": body ?: [NSData data]
};
}
@end
// MARK: - 3. Cached Static File Handler
@interface CachedStaticFileHandler : StaticFileHandler
@property (nonatomic, strong) NSCache<NSString *, NSData *> *fileCache;
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *fileSizes;
- (instancetype)initWithDocumentRoot:(NSString *)root cacheSize:(NSUInteger)count;
@end
@implementation CachedStaticFileHandler
- (instancetype)initWithDocumentRoot:(NSString *)root cacheSize:(NSUInteger)count {
self = [super initWithDocumentRoot:root];
if (self) {
_fileCache = [[NSCache alloc] init];
_fileCache.countLimit = count;
_fileSizes = [NSMutableDictionary dictionary];
}
return self;
}
- (NSData *)serveFileAtPath:(NSString *)path
mimeType:(NSString **)mimeType
statusCode:(NSInteger *)statusCode {
// Check cache first
NSData *cachedData = [self.fileCache objectForKey:path];
if (cachedData) {
NSLog(@"Cache hit: %@", path);
*mimeType = [[MIMETypeDetector sharedDetector] MIMETypeForExtension:[path pathExtension]];
*statusCode = 200;
return cachedData;
}
NSLog(@"Cache miss: %@", path);
// Load from disk
NSData *fileData = [super serveFileAtPath:path
mimeType:mimeType
statusCode:statusCode];
if (*statusCode == 200) {
[self.fileCache setObject:fileData forKey:path];
self.fileSizes[path] = @(fileData.length);
}
return fileData;
}
- (void)clearCache {
[self.fileCache removeAllObjects];
NSLog(@"File cache cleared");
}
- (NSUInteger)getCacheSize {
return self.fileCache.countLimit;
}
@end
// MARK: - 4. File Server with Range Support
@interface RangeAwareFileHandler : StaticFileHandler
- (NSData *)serveFileAtPath:(NSString *)path
range:(NSRange)range
statusCode:(NSInteger *)statusCode
headers:(NSDictionary **)headers;
@end
@implementation RangeAwareFileHandler
- (NSData *)serveFileAtPath:(NSString *)path
range:(NSRange)range
statusCode:(NSInteger *)statusCode
headers:(NSDictionary **)headers {
// Remove leading slash
NSString *relativePath = [path hasPrefix:@"/"] ? [path substringFromIndex:1] : path;
NSString *fullPath = [self.documentRoot stringByAppendingPathComponent:relativePath];
// Get file size
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDictionary *attributes = [fileManager attributesOfItemAtPath:fullPath error:nil];
if (!attributes) {
*statusCode = 404;
return nil;
}
NSUInteger fileSize = [attributes[NSFileSize] unsignedIntegerValue];
// Validate range
if (range.location >= fileSize) {
*statusCode = 416; // Range Not Satisfiable
*headers = @{
@"Content-Range": [NSString stringWithFormat:@"bytes */%lu", (unsigned long)fileSize]
};
return nil;
}
// Adjust range length if needed
if (range.location + range.length > fileSize) {
range.length = fileSize - range.location;
}
// Read file range
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:fullPath];
[fileHandle seekToFileOffset:range.location];
NSData *rangeData = [fileHandle readDataOfLength:range.length];
[fileHandle closeFile];
*statusCode = 206; // Partial Content
*headers = @{
@"Content-Range": [NSString stringWithFormat:@"bytes %lu-%lu/%lu",
(unsigned long)range.location,
(unsigned long)(range.location + range.length - 1),
(unsigned long)fileSize],
@"Content-Length": @(rangeData.length)
};
NSLog(@"Serving range: %lu-%lu of %@",
(unsigned long)range.location,
(unsigned long)(range.location + range.length - 1),
path);
return rangeData;
}
@end
// MARK: - 5. Virtual File System
@interface VirtualFileSystem : NSObject
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSData *> *files;
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSString *> *mimeTypes;
- (void)addVirtualFile:(NSString *)path
content:(NSString *)content
mimeType:(NSString *)mimeType;
- (NSData *)getFileAtPath:(NSString *)path
mimeType:(NSString **)mimeType
statusCode:(NSInteger *)statusCode;
@end
@implementation VirtualFileSystem
- (instancetype)init {
self = [super init];
if (self) {
_files = [NSMutableDictionary dictionary];
_mimeTypes = [NSMutableDictionary dictionary];
// Add default files
[self addVirtualFile:@"/index.html"
content:@"<html><body><h1>Welcome</h1></body></html>"
mimeType:@"text/html"];
[self addVirtualFile:@"/api/status.json"
content:@"{\"status\": \"ok\"}"
mimeType:@"application/json"];
}
return self;
}
- (void)addVirtualFile:(NSString *)path
content:(NSString *)content
mimeType:(NSString *)mimeType {
NSData *fileData = [content dataUsingEncoding:NSUTF8StringEncoding];
self.files[path] = fileData;
self.mimeTypes[path] = mimeType;
NSLog(@"Added virtual file: %@", path);
}
- (NSData *)getFileAtPath:(NSString *)path
mimeType:(NSString **)mimeType
statusCode:(NSInteger *)statusCode {
// Try exact match first
NSData *data = self.files[path];
if (data) {
*mimeType = self.mimeTypes[path];
*statusCode = 200;
return data;
}
// Try with leading slash
NSString *key = [path hasPrefix:@"/"] ? path : [@"/" stringByAppendingString:path];
data = self.files[key];
if (data) {
*mimeType = self.mimeTypes[key];
*statusCode = 200;
return data;
}
// 404
*statusCode = 404;
*mimeType = @"text/plain";
return [@"404 Not Found" dataUsingEncoding:NSUTF8StringEncoding];
}
@end
// MARK: - Main Demonstration
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"=== macOS Objective-C Static Files Examples ===\n");
// 1. MIME type detection
NSLog(@"--- 1. MIME Type Detection ---");
NSArray *testFiles = @[@"index.html", @"styles.css", @"app.js", @"image.png",
@"document.pdf", @"data.json", @"video.mp4"];
for (NSString *file in testFiles) {
NSString *mimeType = [[MIMETypeDetector sharedDetector] MIMETypeForExtension:[file pathExtension]];
NSLog(@"%@ -> %@", file, mimeType);
}
// 2. Basic static file serving
NSLog(@"\n--- 2. Static File Serving ---");
StaticFileHandler *fileHandler = [[StaticFileHandler alloc] initWithDocumentRoot:@"/tmp/static"];
// Create test directory and files
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager createDirectoryAtPath:@"/tmp/static" withIntermediateDirectories:YES attributes:nil error:nil];
NSString *htmlContent = @"<html><head><title>Test</title></head><body><h1>Hello World</h1></body></html>";
[htmlContent writeToFile:@"/tmp/static/index.html"
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
NSString *jsonContent = @"{\"message\": \"Hello from static file\"}";
[jsonContent writeToFile:@"/tmp/static/api/data.json"
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
// Serve files
NSString *mimeType = nil;
NSInteger statusCode = 0;
NSData *htmlData = [fileHandler serveFileAtPath:@"/index.html"
mimeType:&mimeType
statusCode:&statusCode];
NSLog(@"Served /index.html: status %ld, %lu bytes, mime: %@",
(long)statusCode, (unsigned long)htmlData.length, mimeType);
NSData *jsonData = [fileHandler serveFileAtPath:@"/api/data.json"
mimeType:&mimeType
statusCode:&statusCode];
NSLog(@"Served /api/data.json: status %ld, %lu bytes, mime: %@",
(long)statusCode, (unsigned long)jsonData.length, mimeType);
// 404 case
NSData *notFoundData = [fileHandler serveFileAtPath:@"/nonexistent.html"
mimeType:&mimeType
statusCode:&statusCode];
NSLog(@"Served /nonexistent.html: status %ld, %lu bytes",
(long)statusCode, (unsigned long)notFoundData.length);
// 3. Cached static file serving
NSLog(@"\n--- 3. Cached Static File Serving ---");
CachedStaticFileHandler *cachedHandler = [[CachedStaticFileHandler alloc]
initWithDocumentRoot:@"/tmp/static"
cacheSize:100];
// First request (cache miss)
[cachedHandler serveFileAtPath:@"/index.html"
mimeType:&mimeType
statusCode:&statusCode];
// Second request (cache hit)
[cachedHandler serveFileAtPath:@"/index.html"
mimeType:&mimeType
statusCode:&statusCode];
NSLog(@"Cache size: %lu files", (unsigned long)[cachedHandler getCacheSize]);
// 4. Range requests
NSLog(@"\n--- 4. Range Requests ---");
RangeAwareFileHandler *rangeHandler = [[RangeAwareFileHandler alloc]
initWithDocumentRoot:@"/tmp/static"];
// Create a larger file for range testing
NSMutableData *largeFile = [NSMutableData data];
for (int i = 0; i < 1000; i++) {
NSString *line = [NSString stringWithFormat:@"Line %d\n", i];
[largeFile appendData:[line dataUsingEncoding:NSUTF8StringEncoding]];
}
[largeFile writeToFile:@"/tmp/static/large.txt" atomically:YES];
NSDictionary *headers = nil;
NSData *rangeData = [rangeHandler serveFileAtPath:@"/large.txt"
range:NSMakeRange(100, 200)
statusCode:&statusCode
headers:&headers];
NSLog(@"Range request: status %ld, %lu bytes, headers: %@",
(long)statusCode, (unsigned long)rangeData.length, headers);
// 5. Virtual file system
NSLog(@"\n--- 5. Virtual File System ---");
VirtualFileSystem *vfs = [[VirtualFileSystem alloc] init];
[vfs addVirtualFile:@"/about.html"
content:@"<html><body><h1>About</h1></body></html>"
mimeType:@"text/html"];
[vfs addVirtualFile:@"/config.json"
content:@"{\"env\": \"production\", \"debug\": false}"
mimeType:@"application/json"];
NSData *vfsData = [vfs getFileAtPath:@"/about.html"
mimeType:&mimeType
statusCode:&statusCode];
NSLog(@"Virtual file /about.html: status %ld, %lu bytes",
(long)statusCode, (unsigned long)vfsData.length);
// Cleanup
[fileManager removeItemAtPath:@"/tmp/static" error:nil];
NSLog(@"\n=== Static Files Examples Completed ===");
NSLog(@"\nKey features demonstrated:");
NSLog(@" - MIME type detection for common file types");
NSLog(@" - Static file serving from document root");
NSLog(@" - In-memory caching for performance");
NSLog(@" - HTTP Range request support");
NSLog(@" - Virtual file system for testing");
}
return 0;
}
💻 Middleware objectivec
🔴 complex
⭐⭐⭐⭐
Implémenter pipeline middleware traitement requêtes journalisation authentification gestion erreurs
⏱️ 40 min
🏷️ objectivec, macos, web, middleware
Prerequisites:
Advanced Objective-C, Foundation framework
// macOS Objective-C Middleware Examples
// Using Foundation framework
#import <Foundation/Foundation.h>
// MARK: - 1. Request and Response Models
@interface HTTPRequest : NSObject
@property (nonatomic, strong) NSString *method;
@property (nonatomic, strong) NSString *path;
@property (nonatomic, strong) NSDictionary<NSString *, NSString *> *headers;
@property (nonatomic, strong) NSDictionary<NSString *, NSString *> *query;
@property (nonatomic, strong) NSData *body;
@property (nonatomic, strong) NSMutableDictionary<NSString *, id> *userData;
- (instancetype)initWithMethod:(NSString *)method
path:(NSString *)path;
- (void)addHeader:(NSString *)value forKey:(NSString *)key;
@end
@implementation HTTPRequest
- (instancetype)initWithMethod:(NSString *)method
path:(NSString *)path {
self = [super init];
if (self) {
_method = method;
_path = path;
_headers = [NSMutableDictionary dictionary];
_query = [NSMutableDictionary dictionary];
_userData = [NSMutableDictionary dictionary];
}
return self;
}
- (void)addHeader:(NSString *)value forKey:(NSString *)key {
[(NSMutableDictionary *)self.headers setObject:value forKey:key];
}
@end
@interface HTTPResponse : NSObject
@property (nonatomic, assign) NSInteger statusCode;
@property (nonatomic, strong) NSDictionary<NSString *, NSString *> *headers;
@property (nonatomic, strong) NSData *body;
@property (nonatomic, strong) NSString *statusText;
- (instancetype)initWithStatusCode:(NSInteger)statusCode;
- (void)setHeader:(NSString *)value forKey:(NSString *)key;
- (NSString *)description;
@end
@implementation HTTPResponse
- (instancetype)initWithStatusCode:(NSInteger)statusCode {
self = [super init];
if (self) {
_statusCode = statusCode;
_headers = [NSMutableDictionary dictionary];
_statusText = [self statusTextForCode:statusCode];
}
return self;
}
- (void)setHeader:(NSString *)value forKey:(NSString *)key {
[(NSMutableDictionary *)self.headers setObject:value forKey:key];
}
- (NSString *)statusTextForCode:(NSInteger)code {
switch (code) {
case 200: return @"OK";
case 201: return @"Created";
case 204: return @"No Content";
case 400: return @"Bad Request";
case 401: return @"Unauthorized";
case 403: return @"Forbidden";
case 404: return @"Not Found";
case 500: return @"Internal Server Error";
default: return @"Unknown";
}
}
- (NSString *)description {
return [NSString stringWithFormat:@"HTTPResponse(%@ - %ld)", self.statusText, (long)self.statusCode];
}
@end
// MARK: - 2. Middleware Protocol
@protocol Middleware <NSObject>
@required
- (void)processRequest:(HTTPRequest *)request
response:(HTTPResponse *)response
next:(void (^)(void))next;
@end
// MARK: - 3. Logging Middleware
@interface LoggingMiddleware : NSObject <Middleware>
+ (instancetype)sharedMiddleware;
@end
@implementation LoggingMiddleware
+ (instancetype)sharedMiddleware {
static LoggingMiddleware *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[LoggingMiddleware alloc] init];
});
return instance;
}
- (void)processRequest:(HTTPRequest *)request
response:(HTTPResponse *)response
next:(void (^)(void))next {
NSLog(@"\n=== Request ===");
NSLog(@"Method: %@", request.method);
NSLog(@"Path: %@", request.path);
NSLog(@"Headers: %@", request.headers);
NSLog(@"Query: %@", request.query);
NSDate *startTime = [NSDate date];
// Call next middleware
if (next) {
next();
}
NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"\n=== Response ===");
NSLog(@"Status: %ld %@", (long)response.statusCode, response.statusText);
NSLog(@"Duration: %.0f ms", duration * 1000);
NSLog(@"Headers: %@", response.headers);
}
@end
// MARK: - 4. Authentication Middleware
@interface AuthenticationMiddleware : NSObject <Middleware>
@property (nonatomic, strong) NSSet<NSString *> *publicPaths;
+ (instancetype)middlewareWithValidTokens:(NSSet<NSString *> *)tokens;
- (instancetype)initWithValidTokens:(NSSet<NSString *> *)tokens;
@end
@implementation AuthenticationMiddleware
{
NSSet<NSString *> *_validTokens;
}
+ (instancetype)middlewareWithValidTokens:(NSSet<NSString *> *)tokens {
return [[self alloc] initWithValidTokens:tokens];
}
- (instancetype)initWithValidTokens:(NSSet<NSString *> *)tokens {
self = [super init];
if (self) {
_validTokens = tokens;
_publicPaths = [NSSet setWithObjects:@"/", @"/health", @"/ping", nil];
}
return self;
}
- (void)processRequest:(HTTPRequest *)request
response:(HTTPResponse *)response
next:(void (^)(void))next {
// Check if path is public
if ([self.publicPaths containsObject:request.path]) {
NSLog(@"Public path, skipping authentication");
if (next) next();
return;
}
// Get authorization header
NSString *authHeader = request.headers[@"Authorization"];
if (!authHeader) {
NSLog(@"Missing authorization header");
response.statusCode = 401;
[response setHeader:@"application/json" forKey:@"Content-Type"];
return;
}
// Check Bearer token
if ([authHeader hasPrefix:@"Bearer "]) {
NSString *token = [authHeader substringFromIndex:7];
if ([_validTokens containsObject:token]) {
NSLog(@"Authentication successful");
request.userData[@"authenticatedUser"] = @"user_from_token";
if (next) next();
} else {
NSLog(@"Invalid token");
response.statusCode = 401;
NSString *errorBody = @"{\"error\": \"Invalid token\"}";
response.body = [errorBody dataUsingEncoding:NSUTF8StringEncoding];
[response setHeader:@"application/json" forKey:@"Content-Type"];
}
} else {
NSLog(@"Invalid authorization format");
response.statusCode = 401;
[response setHeader:@"application/json" forKey:@"Content-Type"];
}
}
@end
// MARK: - 5. CORS Middleware
@interface CORSMiddleware : NSObject <Middleware>
@property (nonatomic, strong) NSString *allowedOrigin;
@property (nonatomic, strong) NSSet<NSString *> *allowedMethods;
@property (nonatomic, strong) NSSet<NSString *> *allowedHeaders;
+ (instancetype)defaultMiddleware;
- (instancetype)initWithAllowedOrigin:(NSString *)origin;
@end
@implementation CORSMiddleware
+ (instancetype)defaultMiddleware {
return [[self alloc] initWithAllowedOrigin:@"*"];
}
- (instancetype)initWithAllowedOrigin:(NSString *)origin {
self = [super init];
if (self) {
_allowedOrigin = origin;
_allowedMethods = [NSSet setWithObjects:@"GET", @"POST", @"PUT", @"DELETE", @"OPTIONS", nil];
_allowedHeaders = [NSSet setWithObjects:@"Content-Type", @"Authorization", nil];
}
return self;
}
- (void)processRequest:(HTTPRequest *)request
response:(HTTPResponse *)response
next:(void (^)(void))next {
// Add CORS headers
[response setHeader:self.allowedOrigin forKey:@"Access-Control-Allow-Origin"];
// Handle preflight request
if ([request.method isEqualToString:@"OPTIONS"]) {
[response setHeader:[self.allowedMethods allObjects].componentsJoinedByString:@", "]
forKey:@"Access-Control-Allow-Methods"];
[response setHeader:[self.allowedHeaders allObjects].componentsJoinedByString:@", "]
forKey:@"Access-Control-Allow-Headers"];
[response setHeader:@"3600" forKey:@"Access-Control-Max-Age"];
response.statusCode = 204;
NSLog(@"CORS preflight request handled");
return;
}
// Process request
if (next) {
next();
}
// Add CORS headers to response
[response setHeader:self.allowedOrigin forKey:@"Access-Control-Allow-Origin"];
[response setHeader:@"true" forKey:@"Access-Control-Allow-Credentials"];
}
@end
// MARK: - 6. Error Handling Middleware
@interface ErrorHandlingMiddleware : NSObject <Middleware>
+ (instancetype)sharedMiddleware;
@end
@implementation ErrorHandlingMiddleware
+ (instancetype)sharedMiddleware {
static ErrorHandlingMiddleware *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[ErrorHandlingMiddleware alloc] init];
});
return instance;
}
- (void)processRequest:(HTTPRequest *)request
response:(HTTPResponse *)response
next:(void (^)(void))next {
@try {
if (next) {
next();
}
// Check if response has error status
if (response.statusCode >= 400) {
NSLog(@"Error response: %ld", (long)response.statusCode);
// Ensure error response has body
if (!response.body) {
NSString *errorBody = [NSString stringWithFormat:@"{\"error\": \"%@\", \"status\": %ld}",
response.statusText, (long)response.statusCode];
response.body = [errorBody dataUsingEncoding:NSUTF8StringEncoding];
[response setHeader:@"application/json" forKey:@"Content-Type"];
}
}
}
@catch (NSException *exception) {
NSLog(@"Exception in middleware chain: %@", exception.reason);
response.statusCode = 500;
NSString *errorBody = [NSString stringWithFormat:@"{\"error\": \"Internal Server Error\", \"message\": \"%@\"}",
exception.reason];
response.body = [errorBody dataUsingEncoding:NSUTF8StringEncoding];
[response setHeader:@"application/json" forKey:@"Content-Type"];
}
}
@end
// MARK: - 7. Content Type Middleware
@interface ContentTypeMiddleware : NSObject <Middleware>
+ (instancetype)sharedMiddleware;
@end
@implementation ContentTypeMiddleware
+ (instancetype)sharedMiddleware {
static ContentTypeMiddleware *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[ContentTypeMiddleware alloc] init];
});
return instance;
}
- (void)processRequest:(HTTPRequest *)request
response:(HTTPResponse *)response
next:(void (^)(void))next {
// Validate content type for POST/PUT requests
if ([request.method isEqualToString:@"POST"] ||
[request.method isEqualToString:@"PUT"]) {
NSString *contentType = request.headers[@"Content-Type"];
if (!contentType) {
NSLog(@"Missing Content-Type header");
response.statusCode = 400;
return;
}
// Store content type in user data for later use
request.userData[@"contentType"] = contentType;
}
// Set default content type for response if not set
if (next) {
next();
}
if (!response.headers[@"Content-Type"]) {
[response setHeader:@"application/json" forKey:@"Content-Type"];
}
}
@end
// MARK: - 8. Middleware Pipeline
@interface MiddlewarePipeline : NSObject
@property (nonatomic, strong) NSMutableArray<id<Middleware>> *middlewares;
- (instancetype)initWithMiddlewares:(NSArray<id<Middleware>> *)middlewares;
- (void)addMiddleware:(id<Middleware>)middleware;
- (void)processRequest:(HTTPRequest *)request
response:(HTTPResponse *)response
handler:(void (^)(HTTPRequest *, HTTPResponse *))handler;
@end
@implementation MiddlewarePipeline
- (instancetype)initWithMiddlewares:(NSArray<id<Middleware>> *)middlewares {
self = [super init];
if (self) {
_middlewares = [middlewares mutableCopy];
}
return self;
}
- (instancetype)init {
return [self initWithMiddlewares:@[]];
}
- (void)addMiddleware:(id<Middleware>)middleware {
[self.middlewares addObject:middleware];
}
- (void)processRequest:(HTTPRequest *)request
response:(HTTPResponse *)response
handler:(void (^)(HTTPRequest *, HTTPResponse *))handler {
// Create middleware chain
__block NSInteger index = 0;
void (^next)(void) = ^{
if (index < self.middlewares.count) {
id<Middleware> middleware = self.middlewares[index];
index++;
[middleware processRequest:request response:response next:next];
} else if (handler) {
// All middleware processed, call final handler
handler(request, response);
}
};
// Start middleware chain
next();
}
@end
// MARK: - Main Demonstration
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"=== macOS Objective-C Middleware Examples ===\n");
// Create middleware pipeline
NSLog(@"--- Setting up middleware pipeline ---\n");
// Create authentication middleware with valid tokens
NSSet *validTokens = [NSSet setWithObjects:@"secret-token-123", @"admin-token-456", nil];
AuthenticationMiddleware *authMiddleware = [AuthenticationMiddleware middlewareWithValidTokens:validTokens];
// Create middleware pipeline
MiddlewarePipeline *pipeline = [[MiddlewarePipeline alloc] init];
[pipeline addMiddleware:[LoggingMiddleware sharedMiddleware]];
[pipeline addMiddleware:[ContentTypeMiddleware sharedMiddleware]];
[pipeline addMiddleware:authMiddleware];
[pipeline addMiddleware:[CORSMiddleware defaultMiddleware]];
[pipeline addMiddleware:[ErrorHandlingMiddleware sharedMiddleware]];
NSLog(@"Middleware pipeline configured with:");
NSLog(@" 1. Logging Middleware");
NSLog(@" 2. Content Type Middleware");
NSLog(@" 3. Authentication Middleware");
NSLog(@" 4. CORS Middleware");
NSLog(@" 5. Error Handling Middleware\n");
// Test request 1: Public path
NSLog(@"\n=== Test 1: Public path (no auth required) ===");
HTTPRequest *request1 = [[HTTPRequest alloc] initWithMethod:@"GET" path:@"/health"];
[request1 addHeader:@"application/json" forKey:@"Accept"];
HTTPResponse *response1 = [[HTTPResponse alloc] initWithStatusCode:200];
[pipeline processRequest:request1
response:response1
handler:^(HTTPRequest *req, HTTPResponse *resp) {
NSLog(@"Handler called");
resp.statusCode = 200;
NSString *body = @"{\"status\": \"healthy\"}";
resp.body = [body dataUsingEncoding:NSUTF8StringEncoding];
}];
// Test request 2: Protected path with valid token
NSLog(@"\n=== Test 2: Protected path with valid token ===");
HTTPRequest *request2 = [[HTTPRequest alloc] initWithMethod:@"GET" path:@"/api/users"];
[request2 addHeader:@"Bearer secret-token-123" forKey:@"Authorization"];
[request2 addHeader:@"application/json" forKey:@"Accept"];
HTTPResponse *response2 = [[HTTPResponse alloc] initWithStatusCode:200];
[pipeline processRequest:request2
response:response2
handler:^(HTTPRequest *req, HTTPResponse *resp) {
NSLog(@"Handler called");
resp.statusCode = 200;
NSString *body = @"{\"users\": [\"user1\", \"user2\"]}";
resp.body = [body dataUsingEncoding:NSUTF8StringEncoding];
}];
// Test request 3: Protected path without token
NSLog(@"\n=== Test 3: Protected path without token ===");
HTTPRequest *request3 = [[HTTPRequest alloc] initWithMethod:@"GET" path:@"/api/users"];
[request3 addHeader:@"application/json" forKey:@"Accept"];
HTTPResponse *response3 = [[HTTPResponse alloc] initWithStatusCode:200];
[pipeline processRequest:request3
response:response3
handler:^(HTTPRequest *req, HTTPResponse *resp) {
NSLog(@"Handler called (should not reach here)");
resp.statusCode = 200;
}];
// Test request 4: OPTIONS preflight
NSLog(@"\n=== Test 4: CORS preflight request ===");
HTTPRequest *request4 = [[HTTPRequest alloc] initWithMethod:@"OPTIONS" path:@"/api/users"];
[request4 addHeader:@"*" forKey:@"Origin"];
[request4 addHeader:@"POST" forKey:@"Access-Control-Request-Method"];
HTTPResponse *response4 = [[HTTPResponse alloc] initWithStatusCode:200];
[pipeline processRequest:request4
response:response4
handler:^(HTTPRequest *req, HTTPResponse *resp) {
NSLog(@"Handler should not be called for OPTIONS");
}];
// Test request 5: POST with content type
NSLog(@"\n=== Test 5: POST with content type ===");
HTTPRequest *request5 = [[HTTPRequest alloc] initWithMethod:@"POST" path:@"/api/users"];
[request5 addHeader:@"Bearer secret-token-123" forKey:@"Authorization"];
[request5 addHeader:@"application/json" forKey:@"Content-Type"];
NSString *jsonBody = @"{\"name\": \"John\", \"email\": \"[email protected]\"}";
request5.body = [jsonBody dataUsingEncoding:NSUTF8StringEncoding];
HTTPResponse *response5 = [[HTTPResponse alloc] initWithStatusCode:201];
[pipeline processRequest:request5
response:response5
handler:^(HTTPRequest *req, HTTPResponse *resp) {
NSLog(@"Handler called - creating user");
resp.statusCode = 201;
NSString *body = @"{\"id\": \"123\", \"name\": \"John\"}";
resp.body = [body dataUsingEncoding:NSUTF8StringEncoding];
}];
NSLog(@"\n=== Middleware Examples Completed ===");
NSLog(@"\nMiddleware execution order:");
NSLog(@" 1. Request -> Logging Middleware");
NSLog(@" 2. -> Content Type Middleware");
NSLog(@" 3. -> Authentication Middleware");
NSLog(@" 4. -> CORS Middleware");
NSLog(@" 5. -> Error Handling Middleware");
NSLog(@" 6. -> Handler");
NSLog(@" 7. -> Response back through middleware chain");
}
return 0;
}