Moya: Not able to get URLEncoding working correctly when there is "?" and the method is "POST"

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()
        }
    }
 }

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 37 (5 by maintainers)

Commits related to this issue

Most upvoted comments

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”

@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

@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])

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 Did you find any solutions? Have the same problem

@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

@dmitrykurochka I use Alamofire directly for functions that has & in Post methods’s url

@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)

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:

Screen Shot 2020-06-19 at 8 37 57 PM

@mayuur It works because “?” should not be part of the path. It indicates the query. Therefore, the appendingPathComponent method will encode the “?” as “%3F”.

@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<RequestAPI>( 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: [:])

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)
        }
    }

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?

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 😉