-
Notifications
You must be signed in to change notification settings - Fork 497
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add Content-Type to custom encoder/decoder #1309
Conversation
a67008d
to
82b046c
Compare
Sources/Kitura/Router.swift
Outdated
let bestMediaType = MediaType(bestAccepts), | ||
let bestEncoder = encoders[bestMediaType] | ||
else { | ||
let jsonEncoder = encoders[.json] ?? { return JSONEncoder() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than hard-coding a fallback MediaType of .json
could we allow the user to specify this on the Router alongside the list of encoders?
Sources/Kitura/RouterRequest.swift
Outdated
@@ -161,12 +164,28 @@ public class RouterRequest { | |||
/// Initializes a `RouterRequest` instance | |||
/// | |||
/// - Parameter request: the server request | |||
init(request: ServerRequest) { | |||
convenience init(request: ServerRequest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is internal, and it looks like (now that you've changed Router to call the 2-arg initializer) it is unused. I think we could just remove this init altogether.
Sources/Kitura/Router.swift
Outdated
@@ -492,11 +548,16 @@ extension Router : ServerDelegate { | |||
/// - Parameter response: The `ServerResponse` object used to send responses to the | |||
/// HTTP request at the [Kitura-net](http://ibm-swift.github.io/Kitura-net/) API level. | |||
public func handle(request: ServerRequest, response: ServerResponse) { | |||
let routeReq = RouterRequest(request: request) | |||
var decoder: (() -> BodyDecoder)? | |||
if let mediaType = MediaType(headers: request.headers) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should test this to see how much overhead it adds for requests such as a simple GET
that does not contain a content type (or body).
Sources/Kitura/RouterRequest.swift
Outdated
var data = Data() | ||
_ = try serverRequest.read(into: &data) | ||
return try JSONDecoder().decode(type, from: data) | ||
let decoderInstance = decoder ?? JSONDecoder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let decoder = self.decoder ?? JSONDecoder()
Sources/Kitura/RouterResponse.swift
Outdated
@@ -85,7 +85,9 @@ public class RouterResponse { | |||
|
|||
private var lifecycle = Lifecycle() | |||
|
|||
private let encoder = JSONEncoder() | |||
let encoder: BodyEncoder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this remain private
?
Sources/Kitura/RouterResponse.swift
Outdated
} | ||
do { | ||
headers.setType("json") | ||
send(data: try JSONEncoder().encode(json)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be confusing to the user if they customize their JSON encoder that we ignore it and use a default one here.
Sources/Kitura/RouterResponse.swift
Outdated
@@ -572,7 +591,7 @@ extension RouterResponse { | |||
.replacingOccurrences(of: "\u{2029}", with: "\\u2029") | |||
} | |||
|
|||
let jsonStr = String(data: try encoder.encode(jsonp), encoding: .utf8)! | |||
let jsonStr = String(data: try JSONEncoder().encode(jsonp), encoding: .utf8)! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment as above
@@ -53,7 +53,6 @@ extension String { | |||
|
|||
return result | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you undo this whitespace-only change?
@@ -144,4 +144,3 @@ public struct MediaType: CustomStringConvertible, Equatable, Hashable { | |||
/// The hashValue for the MediaTypes. Required for Hashable conformance. | |||
public let hashValue: Int | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
XCTAssertEqual(inDate, codableDate) | ||
respondWith(1, codableDate, nil) | ||
} | ||
customRouter.put("/customCoder") { (id: Int, inDate: CodableDate, respondWith: (CodableDate?, RequestError?) -> Void) in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the refactor we probably only need to test GET and POST now
Sources/Kitura/Router.swift
Outdated
* | ||
* ### Usage Example: ### | ||
* ```swift | ||
* let (mediaType, encoder) = selectResponseEncoder(request) | ||
* ``` | ||
* - Parameter request: The RouterRequest to check | ||
* - Returns: A tuple of the highest rated Encoder, or a JSONEncoder() if no encoders match the Accepts header and it's corresponding MediaType. | ||
* - Returns: A tuple of the highest rated MediaType and it's corresponding Encoder, or a JSONEncoder() if no encoders match the Accepts header and it's corresponding . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's -> its (x2)
Also, you lost the word 'MediaType' here somehow.
I regression tested these changes on an Ubuntu 16.04 box. The original changes regressed performance quite badly (11%) for a simple Hello World which did not require a decoder/encoder. Commit 29d83a2 delays the evaluation of the output encoder until we need one (which resolves this regression). We found a further regression when the request contains an Perf results: Hello World (no Content-Type / Accept headers)
Codable GET (no Content-Type / Accept headers)
Codable POST (no Accept header)
Codable GET (with Accept header)
Codable POST (with Accept header)
|
We are left with a 6-8% regression for a Codable POST (regardless of whether there is an Accept header), and a smaller ~2% regression for a Codable GET. There may be other opportunities to reduce regression which we haven't explored yet. |
Description
This PR adds two fields to Router called encoders and decoders.
These contain a String to BodyEncoderGenerator/BodyDecoderGenerator dictionary.
We have also added a Read(as: Codable, decoder: BodyDecoder) function, which decodes the data from the routerRequest into a codable type using the given Decoder.
The Codable router uses the decoders dictionary to generate a decoder for the Content-type header and then use the new Read function to decode the request using that decoder.
The Codable router uses the Accepts header to select the best encoder from the Encoders dictionary and use that encoder to encode it's response.
Travis pass requirements
This requires a the Query Coders to be able to encode/decoder data which is in this (PR](Kitura/KituraContracts#28)
It also requires the addition of the BodyEncoder/BodyDecoder protocol which is currently being worked on this branch
Motivation and Context
This allows users to customize the JSON encoder and decoder in Codable routes and add their own Encoders/Decoders for encoding/decoding different Content-Type headers.
This is a fix for issue #1298
This follows on from the work in pull request 1221 but is generalized for any custom encoder/decoder
How Has This Been Tested?
This change affects all Codable and Type-Safe routes so tests have been added which use a custom JSONEncoder. The test suite has also been updated to allow custom encoders/decoders to be used.
Checklist: