ソースを参照

Adding HTTP response validation

Mattt Thompson 11 年 前
コミット
3040ba6d0c

+ 4 - 0
Alamofire.xcodeproj/project.pbxproj

@@ -14,6 +14,7 @@
 		F8858DDD19A96B4300F55F93 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8111E5D19A9674D0040E7D1 /* RequestTests.swift */; };
 		F8858DDE19A96B4400F55F93 /* ResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8111E5E19A9674D0040E7D1 /* ResponseTests.swift */; };
 		F897FF4119AA800700AB5182 /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897FF4019AA800700AB5182 /* Alamofire.swift */; };
+		F8AE910219D28DCC0078C7B2 /* ValidationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AE910119D28DCC0078C7B2 /* ValidationTests.swift */; };
 		F8E6024519CB46A800A3E7F1 /* AuthenticationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E6024419CB46A800A3E7F1 /* AuthenticationTests.swift */; };
 /* End PBXBuildFile section */
 
@@ -39,6 +40,7 @@
 		F8111E5E19A9674D0040E7D1 /* ResponseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseTests.swift; sourceTree = "<group>"; };
 		F8111E5F19A9674D0040E7D1 /* UploadTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadTests.swift; sourceTree = "<group>"; };
 		F897FF4019AA800700AB5182 /* Alamofire.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Alamofire.swift; sourceTree = "<group>"; };
+		F8AE910119D28DCC0078C7B2 /* ValidationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationTests.swift; sourceTree = "<group>"; };
 		F8E6024419CB46A800A3E7F1 /* AuthenticationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationTests.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -105,6 +107,7 @@
 				F8111E5F19A9674D0040E7D1 /* UploadTests.swift */,
 				F8111E5B19A9674D0040E7D1 /* DownloadTests.swift */,
 				F8E6024419CB46A800A3E7F1 /* AuthenticationTests.swift */,
+				F8AE910119D28DCC0078C7B2 /* ValidationTests.swift */,
 				F8111E4019A95C8B0040E7D1 /* Supporting Files */,
 			);
 			path = Tests;
@@ -236,6 +239,7 @@
 				F8858DDD19A96B4300F55F93 /* RequestTests.swift in Sources */,
 				F8E6024519CB46A800A3E7F1 /* AuthenticationTests.swift in Sources */,
 				F8858DDE19A96B4400F55F93 /* ResponseTests.swift in Sources */,
+				F8AE910219D28DCC0078C7B2 /* ValidationTests.swift in Sources */,
 				F8111E6119A9674D0040E7D1 /* ParameterEncodingTests.swift in Sources */,
 				F8111E6419A9674D0040E7D1 /* UploadTests.swift in Sources */,
 				F8111E6019A9674D0040E7D1 /* DownloadTests.swift in Sources */,

+ 29 - 4
README.md

@@ -13,17 +13,17 @@ Of course, AFNetworking remains the premiere networking library available for Ma
 - Upload File / Data / Stream
 - Download using Request or Resume data
 - Authentication with NSURLCredential
+- HTTP Response Validation
 - Progress Closure & NSProgress
 - cURL Debug Output
+- Comprehensive Unit Test Coverage
+- Complete Documentation
 
 ### Planned for 1.0 Release*
 
 _* Coming very soon_
 
-- Comprehensive Unit Test Coverage
-- Complete Documentation
-- HTTP Response Validation
-- TLS Chain Validation
+- [ ] TLS Chain Validation
 
 ## Requirements
 
@@ -288,6 +288,31 @@ Alamofire.request(.GET, "https://httpbin.org/basic-auth/\(user)/\(password)")
 }
 ```
 
+### Validation
+
+#### Manual
+
+```swift
+Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
+         .validate(statusCode: 200..<300)
+         .validate(contentType: ["application/json"])
+         .response { (_, _, _, error) in
+                  println(error)
+         }
+```
+
+#### Automatic
+
+Automatically validates status code within `200...299` range, and that the `Content-Type` header of the response matches the `Accept` header of the request, if one is provided.
+
+```swift
+Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
+         .validate()
+         .response { (_, _, _, error) in
+                  println(error)
+         }
+```
+
 ### Printable
 
 ```swift

+ 151 - 13
Source/Alamofire.swift

@@ -22,6 +22,9 @@
 
 import Foundation
 
+/// Alamofire errors
+public let AlamofireErrorDomain = "com.alamofire.error"
+
 /**
     HTTP method definitions.
 
@@ -619,19 +622,13 @@ public class Request {
     */
     public func response(priority: Int = DISPATCH_QUEUE_PRIORITY_DEFAULT, queue: dispatch_queue_t? = nil, serializer: Serializer, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
 
-        dispatch_async(delegate.queue, {
-            dispatch_async(dispatch_get_global_queue(priority, 0), {
-                if var error = self.delegate.error {
-                    dispatch_async(queue ?? dispatch_get_main_queue(), {
-                        completionHandler(self.request, self.response, nil, error)
-                    })
-                } else {
-                    let (responseObject: AnyObject?, serializationError: NSError?) = serializer(self.request, self.response, self.delegate.data)
+        dispatch_sync(delegate.queue, {
+            dispatch_sync(dispatch_get_global_queue(priority, 0), {
+                let (responseObject: AnyObject?, serializationError: NSError?) = serializer(self.request, self.response, self.delegate.data)
 
-                    dispatch_async(queue ?? dispatch_get_main_queue(), {
-                        completionHandler(self.request, self.response, responseObject, serializationError)
-                    })
-                }
+                dispatch_async(queue ?? dispatch_get_main_queue(), {
+                    completionHandler(self.request, self.response, responseObject, self.delegate.error ?? serializationError)
+                })
             })
         })
 
@@ -739,7 +736,10 @@ public class Request {
         }
 
         func URLSession(session: NSURLSession!, task: NSURLSessionTask!, didCompleteWithError error: NSError!) {
-            self.error = error
+            if error != nil {
+                self.error = error
+            }
+
             dispatch_resume(queue)
         }
     }
@@ -805,6 +805,144 @@ public class Request {
     }
 }
 
+// MARK: - Validation
+
+extension Request {
+
+    /**
+        A closure used to validate a request that takes a URL request and URL response, and returns whether the request was valid.
+    */
+    public typealias Validation = (NSURLRequest, NSHTTPURLResponse) -> (Bool)
+
+    /**
+        Validates the request, using the specified closure.
+
+        If validation fails, subsequent calls to response handlers will have an associated error.
+
+        :param: validation A closure to validate the request.
+
+        :returns: The request.
+    */
+    public func validate(validation: Validation) -> Self {
+        return response(priority: DISPATCH_QUEUE_PRIORITY_HIGH, queue: self.delegate.queue, serializer: Request.responseDataSerializer()){ (request, response, data, error) in
+            if response != nil && error == nil {
+                if !validation(request, response!) {
+                    self.delegate.error = NSError(domain: AlamofireErrorDomain, code: -1, userInfo: nil)
+                }
+            }
+        }
+    }
+
+    // MARK: Status Code
+
+    private class func response(response: NSHTTPURLResponse, hasAcceptableStatusCode statusCodes: [Int]) -> Bool {
+        return contains(statusCodes, response.statusCode)
+    }
+
+    /**
+        Validates that the response has a status code in the specified range.
+
+        If validation fails, subsequent calls to response handlers will have an associated error.
+
+        :param: range The range of acceptable status codes.
+
+        :returns: The request.
+    */
+    public func validate(statusCode range: Range<Int>) -> Self {
+        return validate { (_, response) in
+            return Request.response(response, hasAcceptableStatusCode: range.map({$0}))
+        }
+    }
+
+    /**
+        Validates that the response has a status code in the specified array.
+
+        If validation fails, subsequent calls to response handlers will have an associated error.
+
+        :param: array The acceptable status codes.
+
+        :returns: The request.
+    */
+    public func validate(statusCode array: [Int]) -> Self {
+        return validate { (_, response) in
+            return Request.response(response, hasAcceptableStatusCode: array)
+        }
+    }
+
+    // MARK: Content-Type
+
+    private struct MIMEType {
+        let type: String
+        let subtype: String
+
+        init(_ string: String) {
+            let components = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).substringToIndex(string.rangeOfString(";")?.endIndex ?? string.endIndex).componentsSeparatedByString("/")
+
+            self.type = components.first!
+            self.subtype = components.last!
+        }
+
+        func matches(MIME: MIMEType) -> Bool {
+            switch (type, subtype) {
+            case ("*", "*"), ("*", MIME.subtype), (MIME.type, "*"), (MIME.type, MIME.subtype):
+                return true
+            default:
+                return false
+            }
+        }
+    }
+
+    private class func response(response: NSHTTPURLResponse, hasAcceptableContentType contentTypes: [String]) -> Bool {
+        if response.MIMEType != nil {
+            let responseMIMEType = MIMEType(response.MIMEType!)
+            for acceptableMIMEType in contentTypes.map({MIMEType($0)}) {
+                if acceptableMIMEType.matches(responseMIMEType) {
+                    return true
+                }
+            }
+        }
+
+        return false
+    }
+
+    /**
+        Validates that the response has a content type in the specified array.
+
+        If validation fails, subsequent calls to response handlers will have an associated error.
+
+        :param: contentType The acceptable content types, which may specify wildcard types and/or subtypes.
+
+        :returns: The request.
+    */
+    public func validate(contentType array: [String]) -> Self {
+        return validate {(_, response) in
+            return Request.response(response, hasAcceptableContentType: array)
+        }
+    }
+
+    // MARK: Automatic
+
+    /**
+        Validates that the response has a status code in the default acceptable range of 200...299, and that the content type matches any specified in the Accept HTTP header field.
+
+        If validation fails, subsequent calls to response handlers will have an associated error.
+
+        :returns: The request.
+    */
+    public func validate() -> Self {
+        let acceptableStatusCodes: Range<Int> = 200..<300
+        let acceptableContentTypes: [String] = {
+            if let accept = self.request.valueForHTTPHeaderField("Accept") {
+                return accept.componentsSeparatedByString(",")
+            }
+
+            return ["*/*"]
+        }()
+        
+        return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes)
+    }
+}
+
 // MARK: - Upload
 
 extension Manager {

+ 24 - 22
Tests/AuthenticationTests.swift

@@ -30,29 +30,30 @@ class AlamofireAuthenticationTestCase: XCTestCase {
         let password = "password"
         let URL = "http://httpbin.org/basic-auth/\(user)/\(password)"
 
-        let validCredentialsExpectation = expectationWithDescription("\(URL) 200")
         let invalidCredentialsExpectation = expectationWithDescription("\(URL) 401")
 
         Alamofire.request(.GET, URL)
-            .authenticate(user: user, password: password)
+            .authenticate(user: "invalid", password: "credentials")
             .response { (request, response, _, error) in
-                validCredentialsExpectation.fulfill()
+                invalidCredentialsExpectation.fulfill()
 
                 XCTAssertNotNil(request, "request should not be nil")
-                XCTAssertNotNil(response, "response should not be nil")
-                XCTAssert(response?.statusCode == 200, "response status code should be 200")
-                XCTAssertNil(error, "error should be nil")
+                XCTAssertNil(response, "response should be nil")
+                XCTAssertNotNil(error, "error should not be nil")
+                XCTAssert(error?.code == -999, "error should be NSURLErrorDomain Code -999 'cancelled'")
         }
 
+        let validCredentialsExpectation = expectationWithDescription("\(URL) 200")
+
         Alamofire.request(.GET, URL)
-            .authenticate(user: "invalid", password: "credentials")
+            .authenticate(user: user, password: password)
             .response { (request, response, _, error) in
-                invalidCredentialsExpectation.fulfill()
+                validCredentialsExpectation.fulfill()
 
                 XCTAssertNotNil(request, "request should not be nil")
-                XCTAssertNil(response, "response should be nil")
-                XCTAssertNotNil(error, "error should not be nil")
-                XCTAssert(error?.code == -999, "error should be NSURLErrorDomain Code -999 'cancelled'")
+                XCTAssertNotNil(response, "response should not be nil")
+                XCTAssert(response?.statusCode == 200, "response status code should be 200")
+                XCTAssertNil(error, "error should be nil")
         }
 
         waitForExpectationsWithTimeout(10) { (error) in
@@ -66,29 +67,30 @@ class AlamofireAuthenticationTestCase: XCTestCase {
         let password = "password"
         let URL = "http://httpbin.org/digest-auth/\(qop)/\(user)/\(password)"
 
-        let validCredentialsExpectation = expectationWithDescription("\(URL) 200")
         let invalidCredentialsExpectation = expectationWithDescription("\(URL) 401")
 
         Alamofire.request(.GET, URL)
-            .authenticate(user: user, password: password)
+            .authenticate(user: "invalid", password: "credentials")
             .response { (request, response, _, error) in
-                validCredentialsExpectation.fulfill()
+                invalidCredentialsExpectation.fulfill()
 
                 XCTAssertNotNil(request, "request should not be nil")
-                XCTAssertNotNil(response, "response should not be nil")
-                XCTAssert(response?.statusCode == 200, "response status code should be 200")
-                XCTAssertNil(error, "error should be nil")
+                XCTAssertNil(response, "response should be nil")
+                XCTAssertNotNil(error, "error should not be nil")
+                XCTAssert(error?.code == -999, "error should be NSURLErrorDomain Code -999 'cancelled'")
         }
 
+        let validCredentialsExpectation = expectationWithDescription("\(URL) 200")
+
         Alamofire.request(.GET, URL)
-            .authenticate(user: "invalid", password: "credentials")
+            .authenticate(user: user, password: password)
             .response { (request, response, _, error) in
-                invalidCredentialsExpectation.fulfill()
+                validCredentialsExpectation.fulfill()
 
                 XCTAssertNotNil(request, "request should not be nil")
-                XCTAssertNil(response, "response should be nil")
-                XCTAssertNotNil(error, "error should not be nil")
-                XCTAssert(error?.code == -999, "error should be NSURLErrorDomain Code -999 'cancelled'")
+                XCTAssertNotNil(response, "response should not be nil")
+                XCTAssert(response?.statusCode == 200, "response status code should be 200")
+                XCTAssertNil(error, "error should be nil")
         }
 
         waitForExpectationsWithTimeout(10) { (error) in

+ 304 - 0
Tests/ValidationTests.swift

@@ -0,0 +1,304 @@
+// DownloadTests.swift
+//
+// Copyright (c) 2014 Alamofire (http://alamofire.org)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+import Foundation
+import Alamofire
+import XCTest
+
+class AlamofireStatusCodeValidationTestCase: XCTestCase {
+    func testValidationForRequestWithAcceptableStatusCodeResponse() {
+        let URL = "http://httpbin.org/status/200"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate(statusCode: 200..<300)
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNil(error, "error should be nil")
+            }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+    func testValidationForRequestWithUnacceptableStatusCodeResponse() {
+        let URL = "http://httpbin.org/status/404"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate(statusCode: [200])
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNotNil(error, "error should not be nil")
+                XCTAssertEqual(error!.domain, AlamofireErrorDomain, "error should be in Alamofire error domain")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+    func testValidationForRequestWithNoAcceptableStatusCodes() {
+        let URL = "http://httpbin.org/status/201"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate(statusCode: [])
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNotNil(error, "error should not be nil")
+                XCTAssertEqual(error!.domain, AlamofireErrorDomain, "error should be in Alamofire error domain")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+}
+
+class AlamofireContentTypeValidationTestCase: XCTestCase {
+    func testValidationForRequestWithAcceptableContentTypeResponse() {
+        let URL = "http://httpbin.org/ip"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate(contentType: ["application/json"])
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNil(error, "error should be nil")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+    func testValidationForRequestWithAcceptableWildcardContentTypeResponse() {
+        let URL = "http://httpbin.org/ip"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate(contentType: ["*/*"])
+            .validate(contentType: ["application/*"])
+            .validate(contentType: ["*/json"])
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNil(error, "error should be nil")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+    func testValidationForRequestWithUnacceptableContentTypeResponse() {
+        let URL = "http://httpbin.org/xml"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate(contentType: ["application/octet-stream"])
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNotNil(error, "error should not be nil")
+                XCTAssertEqual(error!.domain, AlamofireErrorDomain, "error should be in Alamofire error domain")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+    func testValidationForRequestWithNoAcceptableContentTypeResponse() {
+        let URL = "http://httpbin.org/xml"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate(contentType: [])
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNotNil(error, "error should not be nil")
+                XCTAssertEqual(error!.domain, AlamofireErrorDomain, "error should be in Alamofire error domain")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+}
+
+class AlamofireMultipleValidationTestCase: XCTestCase {
+    func testValidationForRequestWithAcceptableStatusCodeAndContentTypeResponse() {
+        let URL = "http://httpbin.org/ip"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate(statusCode: 200..<300)
+            .validate(contentType: ["application/json"])
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNil(error, "error should be nil")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+    func testValidationForRequestWithUnacceptableStatusCodeAndContentTypeResponse() {
+        let URL = "http://httpbin.org/xml"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate(statusCode: 400..<600)
+            .validate(contentType: ["application/octet-stream"])
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNotNil(error, "error should not be nil")
+                XCTAssertEqual(error!.domain, AlamofireErrorDomain, "error should be in Alamofire error domain")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+}
+
+class AlamofireAutomaticValidationTestCase: XCTestCase {
+    func testValidationForRequestWithAcceptableStatusCodeAndContentTypeResponse() {
+        let URL = NSURL(string: "http://httpbin.org/ip")
+        let mutableURLRequest = NSMutableURLRequest(URL: URL)
+        mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Accept")
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate()
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNil(error, "error should be nil")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+    func testValidationForRequestWithUnacceptableStatusCodeResponse() {
+        let URL = "http://httpbin.org/status/404"
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate()
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNotNil(error, "error should not be nil")
+                XCTAssertEqual(error!.domain, AlamofireErrorDomain, "error should be in Alamofire error domain")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+
+    func testValidationForRequestWithAcceptableWildcardContentTypeResponse() {
+        let URL = NSURL(string: "http://httpbin.org/ip")
+        let mutableURLRequest = NSMutableURLRequest(URL: URL)
+        mutableURLRequest.setValue("application/*", forHTTPHeaderField: "Accept")
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate()
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNil(error, "error should be nil")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+    func testValidationForRequestWithAcceptableComplexContentTypeResponse() {
+        let URL = NSURL(string: "http://httpbin.org/xml")
+        let mutableURLRequest = NSMutableURLRequest(URL: URL)
+        mutableURLRequest.setValue("text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8,*/*;q=0.5", forHTTPHeaderField: "Accept")
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate()
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNil(error, "error should be nil")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+
+    func testValidationForRequestWithUnacceptableContentTypeResponse() {
+        let URL = NSURL(string: "http://httpbin.org/xml")
+        let mutableURLRequest = NSMutableURLRequest(URL: URL)
+        mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Accept")
+
+        let expectation = expectationWithDescription("\(URL)")
+
+        Alamofire.request(.GET, URL)
+            .validate()
+            .response { (_, _, _, error) in
+                expectation.fulfill()
+
+                XCTAssertNil(error, "error should be nil")
+        }
+
+        waitForExpectationsWithTimeout(10) { (error) in
+            XCTAssertNil(error, "\(error)")
+        }
+    }
+}