I need to make a POST request with query string and json body. How can I pass each parameters to Moya?
Hey there! Great question. We cover a bit about parameter encoding in the docs, but the short-and-simple answer is that you need to pass a custom endpointClosure when you create the provider. It takes a target, which you switch on to customize individual endpoints.
The parameter encoding you're describing is pretty custom – query string in the URL and a JSON body – so you should use the .Custom parameter encoding type. It takes a closure that modifies the NSURLRequest and returns it. You can see the source here.
Like I said, it's a pretty custom thing so I'm afraid to say we don't have many examples on it yet. We'll help you figure it out, though! And once we do, it would be :100: if you wanted to submit a pull request that adds some more examples to the docs based on what your experience :cake:
Thanks, I'll try it!
Hey, just following up! Anything I can to do help?
Like this?
let compositeParameterEncodingClosure: (URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?) = { request, parameters in
guard let parameters = parameters else {
return (request.URLRequest, nil)
}
let (queryRequest, _) = ParameterEncoding.URL.encode(request, parameters: parameters["query"] as! [String: AnyObject])
let (bodyRequest, _) = ParameterEncoding.JSON.encode(request, parameters: parameters["body"] as! [String: AnyObject])
let compositeRequest = queryRequest.mutableCopy() as! NSMutableURLRequest
compositeRequest.HTTPBody = bodyRequest.HTTPBody
return (compositeRequest, nil)
}
Looks good to me! I must admit I've not tried this before, but it looks like it should work :+1:
Thanks!
The current version of this solution is:
let compositeParameterEncodingRequestClosure(endpoint: Endpoint<YourTarget>, closure: NSURLRequest -> Void) -> Void {
guard let parameters = endpoint.parameters else {
return closure(endpoint.urlRequest)
}
let (queryRequest, _) = ParameterEncoding.URL.encode(endpoint.urlRequest, parameters: (parameters["query"] as! [String: AnyObject]))
let (bodyRequest, _) = ParameterEncoding.JSON.encode(endpoint.urlRequest, parameters: (parameters["body"] as! [String: AnyObject]))
let compositeRequest = queryRequest.mutableCopy() as! NSMutableURLRequest
compositeRequest.HTTPBody = bodyRequest.HTTPBody
return closure(compositeRequest)
}
and use it as the requestClosure of your provider.
I don't know why but I can't build my url with parameter["query"]
ex: url is example.com, parameter["query"] = ["q": "wane"]
after ParameterEncoding.URL.encode(endpoint.urlRequest, parameters: (parameters["query"] as! [String: AnyObject]))
the url will still be example.com but not example.com?q=wane
I end up change the code to
let compositeParameterEncodingClosure: (URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?) = { request, parameters in
guard let parameters = parameters else {
return (request.URLRequest, nil)
}
let (bodyRequest, _) = ParameterEncoding.JSON.encode(request, parameters: parameters["body"] as? [String: AnyObject])
let compositeRequest = bodyRequest.mutableCopy() as! NSMutableURLRequest
compositeRequest.HTTPBody = bodyRequest.HTTPBody
if let query = parameters["query"] as? [String: String],
let url = request.URLRequest.URL?.absoluteString,
var urlComponent = NSURLComponents(string: url) {
urlComponent.queryItems = query.flatMap {
NSURLQueryItem.init(name: $0.0, value: $0.1)
}
compositeRequest.URL = urlComponent.URL
}
return (compositeRequest, nil)
}
This is my solution for Moya 8:
import Moya
import Alamofire
public struct CompositeEncoding: ParameterEncoding {
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
guard let parameters = parameters else {
return try urlRequest.asURLRequest()
}
let queryParamters = (parameters["query"] as! Parameters)
let bodyParameters = (parameters["body"] as! Parameters)
let queryRequest = try URLEncoding(destination: .queryString).encode(urlRequest, with: queryParamters)
let bodyRequest = try JSONEncoding().encode(urlRequest, with: bodyParameters)
var compositeRequest = bodyRequest
compositeRequest.url = queryRequest.url
return compositeRequest
}
}
In case anyone wants to perform: [only Query] or [Query + Body] (application/json) request:
struct CompositeEncoding: ParameterEncoding {
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
guard let parameters = parameters else {
return try urlRequest.asURLRequest()
}
let queryParameters = (parameters["query"] as! Parameters)
let queryRequest = try URLEncoding(destination: .queryString).encode(urlRequest, with: queryParameters)
if let body = parameters["body"] {
let bodyParameters = (body as! Parameters)
var bodyRequest = try JSONEncoding().encode(urlRequest, with: bodyParameters)
bodyRequest.url = queryRequest.url
return bodyRequest
} else {
return queryRequest
}
}
}
public var parameterEncoding: ParameterEncoding {
return CompositeEncoding() // For all requests composite encoding.
}
public var parameters: [String: Any]? {
var params:[String: Any] = [:]
params["query"] = ["token": API_TOKEN, "appId":API_APP_ID, "appKey":API_APP_KEY]
switch self {
case .validatePhone(let phone):
params["body"] = ["phone":phone] // Setting body attribute finally makes request application/json
break
default: break
}
return params
}
Just a heads up:
Once my PR #1147 gets merged then a solution to this issue will be built into Moya via the new requestCompositeParameters(bodyParameters:bodyEncoding:urlParameters:) task case.
You will be able to write something like the following (instead of parameters and parameterEncoding):
var task: Task {
switch self {
case .profile(let username, let language):
return .requestCompositeParameters(bodyParameters: ["username: username], bodyEncoding: JSONEncoding.default, urlParameters: ["lang": language])
default:
return .requestPlain
}
}
Most helpful comment
Just a heads up:
Once my PR #1147 gets merged then a solution to this issue will be built into Moya via the new
requestCompositeParameters(bodyParameters:bodyEncoding:urlParameters:)task case.You will be able to write something like the following (instead of
parametersandparameterEncoding):