Skip to content

Commit b897b09

Browse files
authored
Merge pull request #127 from devlucky/fix/RouterHandlerSameUrlDifferentMethod
Fix Router to handle same url with different HTTP methods
2 parents f48cb36 + 11c17c6 commit b897b09

File tree

4 files changed

+99
-12
lines changed

4 files changed

+99
-12
lines changed

Diff for: .swiftlint.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,7 @@ variable_name:
4747
- db
4848
- HTTPBody
4949
- HTTPHeaders
50-
50+
- lhs
51+
- rhs
52+
5153
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit)

Diff for: Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### HEAD
44
--------------
55

6+
- Fix `Router` to handle same url with different HTTP methods
67

78
### 1.0.0
89
------------

Diff for: Source/Router.swift

+31-11
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,25 @@ public struct Response: ResponseFieldsProvider {
9595
*/
9696
public final class Router {
9797

98-
private typealias Route = (method: HTTPMethod, handler: RouteHandler)
98+
private class Route: Hashable {
99+
let path: String
100+
let method: HTTPMethod
101+
102+
init(path: String, method: HTTPMethod) {
103+
self.path = path
104+
self.method = method
105+
}
106+
107+
private var hashValue: Int {
108+
return path.hashValue ^ method.hashValue
109+
}
110+
}
99111

100112
private enum HTTPMethod: String {
101113
case GET, POST, PUT, DELETE
102114
}
103115

104-
private var routes: [String : Route] = [:]
116+
private var routes: [Route : RouteHandler] = [:]
105117

106118
/// The `baseURL` of the Router
107119
public let baseURL: String
@@ -163,8 +175,8 @@ public final class Router {
163175
guard let requestURL = request.URL
164176
where requestURL.absoluteString?.containsString(baseURL) ?? false else { return false }
165177

166-
for (key, route) in routes where route.method.rawValue == request.HTTPMethod {
167-
if matchRoute(baseURL, path: key, requestURL: requestURL) != nil {
178+
for (key, _) in routes where key.method.rawValue == request.HTTPMethod {
179+
if matchRoute(baseURL, path: key.path, requestURL: requestURL) != nil {
168180
return true
169181
}
170182
}
@@ -180,14 +192,14 @@ public final class Router {
180192
var headerFields: [String : String]? = ["Content-Type": "application/json"]
181193
var serializableObject: Serializable?
182194

183-
for (key, route) in routes {
184-
if let info = matchRoute(baseURL, path: key, requestURL: requestURL) {
195+
for (key, handler) in routes where key.method.rawValue == server.request.HTTPMethod {
196+
if let info = matchRoute(baseURL, path: key.path, requestURL: requestURL) {
185197
// If the request body is nil use `NSURLProtocol` property see swizzling in `NSMutableURLRequest.m`
186198
// using a literal string because a bridging header in the podspec will be more problematic.
187199
let dataBody = server.request.HTTPBody ?? NSURLProtocol.propertyForKey("kkp_requestHTTPBody", inRequest: server.request) as? NSData
188200

189201
let request = Request(components: info.components, queryParameters: info.queryParameters, HTTPBody: dataBody, HTTPHeaders: server.request.allHTTPHeaderFields)
190-
serializableObject = route.handler(request)
202+
serializableObject = handler(request)
191203
break
192204
}
193205
}
@@ -217,6 +229,10 @@ public final class Router {
217229
}
218230
}
219231
}
232+
233+
private func addRoute(with path: String, method: HTTPMethod, handler: RouteHandler) {
234+
routes[Route(path: path, method: method)] = handler
235+
}
220236

221237
/**
222238
Registers a GET request with the given path.
@@ -237,7 +253,7 @@ public final class Router {
237253
- parameter handler: A `RouteHandler` handler that will be used when the route is matched for a GET request
238254
*/
239255
public func get(path: String, handler: RouteHandler) {
240-
routes[path] = (.GET, handler)
256+
addRoute(with: path, method: .GET, handler: handler)
241257
}
242258

243259
/**
@@ -259,7 +275,7 @@ public final class Router {
259275
- parameter handler: A `RouteHandler` handler that will be used when the route is matched for a GET request
260276
*/
261277
public func post(path: String, handler: RouteHandler) {
262-
routes[path] = (.POST, handler)
278+
addRoute(with: path, method: .POST, handler: handler)
263279
}
264280

265281
/**
@@ -281,7 +297,7 @@ public final class Router {
281297
- parameter handler: A `RouteHandler` handler that will be used when the route is matched for a GET request
282298
*/
283299
public func del(path: String, handler: RouteHandler) {
284-
routes[path] = (.DELETE, handler)
300+
addRoute(with: path, method: .DELETE, handler: handler)
285301
}
286302

287303
/**
@@ -303,7 +319,11 @@ public final class Router {
303319
- parameter handler: A `RouteHandler` handler that will be used when the route is matched for a GET request
304320
*/
305321
public func put(path: String, handler: RouteHandler) {
306-
routes[path] = (.PUT, handler)
322+
addRoute(with: path, method: .PUT, handler: handler)
307323
}
308324

309325
}
326+
327+
private func == (lhs: Router.Route, rhs: Router.Route) -> Bool {
328+
return lhs.path == rhs.path && lhs.method == rhs.method
329+
}

Diff for: Tests/RouterTests.swift

+64
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,70 @@ class RouterTests: QuickSpec {
213213
expect(usersCommentsResponseURL?.absoluteString).toEventually(equal("http://www.test.com/users/1/comments/2?page=2&author=hector"))
214214
}
215215

216+
it("should call handlers with same path but different http methods") {
217+
var calledPost = false
218+
var calledPut = false
219+
var calledDel = false
220+
221+
router.post("/users/:user_id") { (request) -> Serializable? in
222+
calledPost = true
223+
return nil
224+
}
225+
226+
router.put("/users/:user_id") { (request) -> Serializable? in
227+
calledPut = true
228+
return nil
229+
}
230+
231+
router.del("/users/:user_id") { (request) -> Serializable? in
232+
calledDel = true
233+
return nil
234+
}
235+
236+
var request = NSMutableURLRequest(URL: NSURL(string: "http://www.test.com/users/1")!)
237+
request.HTTPMethod = "POST"
238+
NSURLSession.sharedSession().dataTaskWithRequest(request) { (_, _, _) in }.resume()
239+
240+
expect(calledPost).toEventually(beTrue())
241+
242+
request = NSMutableURLRequest(URL: NSURL(string: "http://www.test.com/users/1")!)
243+
request.HTTPMethod = "PUT"
244+
NSURLSession.sharedSession().dataTaskWithRequest(request) { (_, _, _) in }.resume()
245+
246+
expect(calledPut).toEventually(beTrue())
247+
248+
request = NSMutableURLRequest(URL: NSURL(string: "http://www.test.com/users/1")!)
249+
request.HTTPMethod = "DELETE"
250+
NSURLSession.sharedSession().dataTaskWithRequest(request) { (_, _, _) in }.resume()
251+
252+
expect(calledDel).toEventually(beTrue())
253+
}
254+
255+
it("should replace handlers with same path and http methods") {
256+
var calledFirstPost = false
257+
var calledSecondPost = false
258+
259+
router.post("/users/:user_id") { (request) -> Serializable? in
260+
calledFirstPost = true
261+
return nil
262+
}
263+
264+
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.test.com/users/1")!)
265+
request.HTTPMethod = "POST"
266+
NSURLSession.sharedSession().dataTaskWithRequest(request) { (_, _, _) in }.resume()
267+
268+
expect(calledFirstPost).toEventually(beTrue())
269+
270+
router.post("/users/:user_id") { (request) -> Serializable? in
271+
calledSecondPost = true
272+
return nil
273+
}
274+
275+
NSURLSession.sharedSession().dataTaskWithRequest(request) { (_, _, _) in }.resume()
276+
277+
expect(calledSecondPost).toEventually(beTrue())
278+
}
279+
216280
context("when the Router has latency") {
217281
it("should delay the mocked response") {
218282
var responseData: NSData? = nil

0 commit comments

Comments
 (0)