I am using the last version of Moya and Alamofire with Swift 3
I am having trouble when the method is "POST" and there is url encoding
The url must be:
products?id_category=28
but it is converted to be:
products%3Fid_category=28
which is not working and giving 400 error on the server
where can I find this conversion and stop it for the requests ?
Here is the code i used:
enum MyService {
case products(categorytId: Int)
}
extension MyService : TargetType {
var base: String { return Constants.MyServiceURL }
var baseURL: URL { return URL(string: base)! }
public var task: Task {
return .request
}
var path : String {
switch self {
// i tried with and without them, changing the parameters down below
case .products(let id_category):
return "products?id_category=\(id_category)"
}
}
var parameterEncoding: ParameterEncoding {
switch self {
case .products:
//return MyURLEncoding.queryString
return URLEncoding.queryString
default:
return JSONEncoding.default
}
}
var parameters : [String : Any]? {
switch self {
// here I tried to add them for Testing purpose, i tried with and without them
case .products(let id_category):
var params: [String: Any] = [:]
params["id_category"] = id_category
return params
default:
return nil
}
}
// The method must be always post always
var method : Moya.Method {
return .post;
}
var sampleDate : Data {
return Data()
}
}
}
Hey @iballan could you provide more details on how you're encoding the parameters in the URL? Are you trying to send parameters in the URL and in the request body as well?
It'd be great if you could share some code that illustrates the issue :wink:
@pedrovereza i have updated the comment above. I think if the method is POST, the query parameters are not added to the URL even if the URLEncoding.queryString is returned in the ParameterEncoding. Right ?
@iballan Can you change your implementation of path to be:
var path : String {
switch self {
case .products(let id_category):
return "products"
}
}
and parameterEncoding to:
var parameterEncoding: ParameterEncoding {
switch self {
case .products:
return URLEncoding.default
default:
return JSONEncoding.default
}
}
These changes will leave your code similar to the example we have in the docs on how to send parameters in the URL when doing a POST request (see .updateUser)
@pedrovereza I have tried exactly what in the example you have mentioned, The parameters are not added at all to the request.
["Moya_Logger: [28/03/2017 19:47:25] Request: http://myservice/products", "Moya_Logger: [28/03/2017 19:47:25]]
Request Headers: [ \"Content-Type\": \"application/x-www-form-urlencoded; charset=utf-8\"]"
"Moya_Logger: [28/03/2017 19:47:25] HTTP Request Method: POST"]
["Moya_Logger: [28/03/2017 19:47:25] Response: <NSHTTPURLResponse: 0x7a82a150> { URL: http://myservice/products }
{ status code: 404, headers {\n \"Cache-Control\" = \"no-cache\";\n \"Content-Length\" = 216;\n \"Content-Type\" = \"application/json; charset=utf-8\";\n Date = \"Tue, 28 Mar 2017 16:47:18 GMT\";\n Expires = \"-1\";\n Pragma = \"no-cache\";\n Server = \"Microsoft-IIS/8.5\";\n \"X-AspNet-Version\" = \"4.0.30319\";\n \"X-Powered-By\" = \"ASP.NET\";\n} }"]
Status Code: 404, Data Length: 216
@iballan try this, I recently had the same issues and solved them by using the following (I forget where, but there was another issue raised in Moya where I cobbled together this answer, wish I could give that author credit):
```swift
var parameterEncoding : Moya.ParameterEncoding {
switch self {
case .feedback:
return TokenURLEncoding.default
default:
return URLEncoding.default
}
}
/// Used to append the Token to requests which require it.
/// Don't attempt to do this in the path variable, as our server can't handle % encoding..
struct TokenURLEncoding: Moya.ParameterEncoding {
public static var default: TokenURLEncoding { return TokenURLEncoding() }
/// Creates a URL request by encoding parameters and applying them onto an existing request.
///
/// - parameter urlRequest: The request to have parameters applied.
/// - parameter parameters: The parameters to apply.
///
/// - throws: An AFError.parameterEncodingFailed error if encoding fails.
///
/// - returns: The encoded request.
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var req = try urlRequest.asURLRequest()
// first coerce a mutable URL out of the urlRequest
// next, get the components of the request
// finally, get our token
guard let request = (req as NSURLRequest).mutableCopy() as? NSMutableURLRequest,
let components = NSURLComponents(string: request.url!.absoluteString),
let token = LinesSession().token else {
// Handle the error
return req
}
let json = try JSONSerialization.data(withJSONObject: parameters!,
options: JSONSerialization.WritingOptions.prettyPrinted)
let tokenQueryItem = NSURLQueryItem(name: "token", value: token)
components.queryItems = [tokenQueryItem as URLQueryItem]
req.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
req.url = components.url
req.httpBody = json
return req
}
}
@avicks the query is not added at all still to the url. I think it has something to do with the method type "Post". btw, i put debug point inside the function you have written and used it. the break point is never reached
I changed the request type to "Get", and the query was added correctly. but I need to change it to post to work correctly
also i have tested code with Alamofire and it works:
let url = "\(Constants.MyService)products?id_category=30"
Alamofire.request(url, method: .post)
.responseData { response in
print(response.request as Any) // original URL request
print(response.response as Any) // URL response
print(response.result.value as Any) // result of response serialization
}
@iballan The query isn't added?
I'm using that encoding I posted to append my query item to the URL, making a POST request, and getting success.
Would you mind elaborating on the structure behind your call?
This issue has been marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
@avicks
@pedrovereza
the problem is instead of getting this as
URL: "http://myWebSErvice/products?id_category=30"
I get this: %3F
URL: "http://myWebSErvice/products%3Fid_category=30"
and when the "?" becomes "%3F" , the webservice throws error this "A potentially dangerous Request.Path value was detected from the client (?)."
What i want is only to send the request with ? not with %3F
Hey @iballan, we recently noticed that the documentation I pointed you to was wrong (we fixed in #1120). 馃槄
Could you try using URLEncoding.queryString instead of default?
@pedrovereza Thank you so much for your help.
However, It is still not working :(
You may check my Question, i already used URLEncoding.queryString
I tried 2 ways:
First: setting the query manually:
var path : String {
switch self {
case .products(let id_category):
return "products?id_category=\(id_category)"
}
}
_Fail_: because ? was encoded into %3F. which caused webservice 400 error.
Second: As you mentioned URLEncoding.queryString
var path : String {
switch self {
case .products:
return "products"
}
}
var parameterEncoding: ParameterEncoding {
switch self {
case .products:
//return MyURLEncoding.queryString
return URLEncoding.queryString
default:
return JSONEncoding.default
}
}
var parameters : [String : Any]? {
switch self {
// here I tried to add them for Testing purpose, i tried with and without them
case .products(let id_category):
var params: [String: Any] = [:]
params["id_category"] = id_category
return params
default:
return nil
}
}
_Fail_: Not adding the parameter at all.
What made me think that it is Moya's issue, when I use Alamofire as I mentioned above the URL is encoded correctly and did not change ? to %3F.
Now in my code I used both Moya (because i already started with it and it is a really overhead work to roll back from it) and Alamofire (to get the POST requests' url query working correctly) 馃憥
I have set up an example on how to send parameters in the URL using POST. Can you try this one out and make sure it works for you? 馃
Hi everybody, today is my first time to use Moya "it is really great library", in fact I got the same problem when i use path with this received character "?" I decided to update in the Moya even is not recommended and I'm happy to share my solution with you.
the only thing I've done is to update inside this file MoyaProvider+Defaults.swift.
update this function :
private final class func url(for target: Target) -> URL {
if target.path.isEmpty {
return target.baseURL
}
return target.baseURL.appendingPathComponent(target.path)
}
with this 馃憤
private final class func url(for target: Target) -> URL {
if target.path.isEmpty {
return target.baseURL
}
let urlComponents = NSURLComponents(url: target.baseURL, resolvingAgainstBaseURL: true)!
let charset = CharacterSet(charactersIn: "?")
if target.path.rangeOfCharacter(from: charset) != nil {
let index = target.path.characters.index(of: "?")
if let index = index {
let path = target.path.substring(to: target.path.characters.index(before: index))
let query = target.path.substring(from: target.path.characters.index(after: index))
urlComponents.path = path
urlComponents.query = query
}
}
else{
urlComponents.path = target.path
}
return urlComponents.url!
}
I hope it will help :)
I've tried the above modification but it doesn't work with my other url paths without "?". So with a slight modification this works for me now
private final class func url(for target: Target) -> URL {
if target.path.isEmpty {
return target.baseURL
}
let urlComponents = NSURLComponents(url: target.baseURL, resolvingAgainstBaseURL: true)!
let index = target.path.characters.index(of: "?")
if let index = index {
let path = target.path.substring(to: index)
let query = target.path.substring(from: target.path.characters.index(after: index))
urlComponents.path = path
urlComponents.query = query
return urlComponents.url!
} else {
return target.baseURL.appendingPathComponent(target.path)
}
}
hi @jx2359 I updated my code you can check agin it will work with the other url also
hi @chivalrousbob, I tried your code again but I got an exception at the last line when the url has no parameters in the path.
I forgot to add that in my previous comment I've also modified the line where "path" is generated. target.path.substring(to: target.path.characters.index(before: index)) returns the path with the last character removed, and it results in a 404 response.
I have same issues.
@iballan You have to write custom endpoints like this:
print("baseURL:(target.baseURL)\n path:(target.path)")
let url = target.baseURL.absoluteString + target.path
print("url:(url)")
//http://XXXX/api/xlogin.ashx?action=xulogin
//This method will escape special characters
//let url = target.baseURL.appendingPathComponent(target.path).absoluteString
//http://XXXX/api/xlogin.ashx%3Faction=xulogin
let endpoint = Endpoint
url: url,
sampleResponseClosure: { .networkResponse(200, target.sampleData) },
method: target.method,
parameters: target.parameters,
parameterEncoding: target.parameterEncoding
)
//Set up your header information
return endpoint.adding(newHTTPHeaderFields: [:])
@iballan Did you find any solutions? Have the same problem
@dmitrykurochka I use Alamofire directly for functions that has & in Post methods's url
@iballan Looks like I have found solution. You should use own Endpoint and url stay correct without changing from '?' to '%3F'
This my real method that works for this situation
let authPlugin = AccessTokenPlugin(token: token)
let provider = RxMoyaProvider<Service>(endpointClosure: {target in
return Endpoint(
url: "\(target.baseURL)\(target.path)",
sampleResponseClosure: { .networkResponse(200, target.sampleData) },
method: target.method,
parameters: target.parameters,
parameterEncoding: target.parameterEncoding
)
}, plugins: [authPlugin])
@dmitrykurochka
Yes, I guess the whole problem was because I used the same function, without the last parameter:
parameterEncoding: target.parameterEncoding
by adding it to the endpoint constructor now it works
This issue has been marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
try use this
var baseURL: URL{
switch self {
case .chagePassword(_,_,let token):
return URL(string: "base_url?token=\(token)")!
default:
return URL(string: "base_url")!
}
}
var path:String{
switch self {
case .chagePassword:
return "/api/update-password"
}
}
and url request will like this "base_url/api/update-password?token="114149814189"
This issue has been marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
This issue has been auto-closed because there hasn't been any activity for at least 21 days. However, we really appreciate your contribution, so thank you for that! 馃檹 Also, feel free to open a new issue if you still experience this problem 馃憤.
@noizar your solution works! but how? I am curious.
@mayuur It works because "?" should not be part of the path. It indicates the query. Therefore, the appendingPathComponent method will encode the "?" as "%3F".
For me it didn't get yet.
The difference is that I'm using .get
But the problem still happens, it converts the "?" to "%3F"
@iballan i have same problem ,can you send me a example how to save this problem? thx
@noizar it works for me
I want to call this https://www.blaalb.com/restApi/testApi/myOrders?number=1&month=24
enum MyWebService {
switch self {
case getMyOrders(number: Int, month: Int)
}
}
var path: String {
switch self {
case . getMyOrders:
return "/myOrders"
}
}
}
var method: Moya.Method {
switch self {
case .getMyOrders:
return .get
}
}
var task: Task {
switch self {
case . getMyOrders(let number, let month):
let p = ["number" : number,
"month": month]
return .requestParameters(parameters:p , encoding: URLEncoding.default)
}
}
鎴戞兂绉拌繖涓负 https://www.blaalb.com/restApi/testApi/myOrders?number=1&month=24
enum MyWebService { switch self { case getMyOrders(number: Int, month: Int) } } var path: String { switch self { case . getMyOrders: return "/myOrders" } } } var method: Moya.Method { switch self { case .getMyOrders: return .get } } var task: Task { switch self { case . getMyOrders(let number, let month): let p = ["number" : number, "month": month] return .requestParameters(parameters:p , encoding: URLEncoding.default) } }
浣犻渶瑕佽嚜瀹氫箟EndpointClosure
private let myEndpointClosure = { (target: MultiTarget) -> Endpoint in
let url = target.baseURL.absoluteString + target.path
let endpoint = Endpoint(url: url,
sampleResponseClosure: { .networkResponse(200, target.sampleData) },
method: target.method,
task: target.task,
httpHeaderFields: target.headers)
return endpoint
}
try use this
var baseURL: URL{ switch self { case .chagePassword(_,_,let token): return URL(string: "base_url?token=\(token)")! default: return URL(string: "base_url")! } } var path:String{ switch self { case .chagePassword: return "/api/update-password" } }and url request will like this "base_url/api/update-password?token="114149814189"
tks u for this 馃憤
Hi everyone, if your full URL is like this:
https://www.yourdomain.com/api/home_gp/api_180913.php?request=login
then you have to use the following solution, it works for me.
your base URL will be this https://www.yourdomain.com/api/home_gp/api_180913.php and you have to pass the request endpoint as a parameter like below:

Most helpful comment
try use this
and url request will like this "base_url/api/update-password?token="114149814189"