Moya: Post request with parameters in both body and url

Created on 9 Jan 2017  路  10Comments  路  Source: Moya/Moya

I am not entirely sure if that is an issue but please feel free to prove me wrong.

What I am trying to achieve is to make a POST request to an API. The first thing is that I have to provide the TOKEN for the API, encodend in the url like:

http://awesomeapi.com?token=mytoken

Unfortunately the API I am calling, is made in a way so that it accepts the token only if it's a URL encoded parameter.

Next thing is I have to send a parameters in a JSON body like :

{
    param1: value,
    param2: value
}

So in general I have to make a POST request and provide both URL encoded parameters and body parameters.

While researching about an answer I came into Issue 431, where the author is trying to achieve the same, but I can not see a straightforward answer to his question.

Reading the docs make me think that having parameters in the request body and as URL encoded is of course possible in separate requests, but having them combined in one is not or at least not documented yet. Am I right about that ? If not, can you provide an example of how can this be done ?

question

Most helpful comment

@kristiyandobrev I think what you're looking for is what @scottrhoyt suggested:

I might prefer searching for a token parameter to in parameters to url encode and then encode the rest as JSON.

Basically, if you have an endpoint like:

enum API {
    case example(id:String, name:String, lastName: String)
}

In order to make a request to api/resource/:id passing name and lastName as json body, you have to:

  • Encode id on the url
    public var path: String {
        switch self {
        case .example(let id, _, _):
            return "api/resource/\(id)"
        }
    }
  • Pass name and lastName as parameters
    public var parameters: [String : Any]? {
        switch self {
        case .example(let _, let name, let lastName):
            return ["name": name, "lastName": lastName]
        }
    }
  • Encode parameters as json
    public var parameterEncoding: Moya.ParameterEncoding { 
        switch self {
        case .example:
            return JSONEncoding.default 
        }
   }

All 10 comments

You can change the used ParameterEncoding. An idea could be to create a ParameterEncoding like:

public struct TokenEncoding: ParameterEncoding {
    public let token: String
    private let urlEncoding = URLEncoding(destination: .methodDependent)

    init(token: String) {
        self.token = token
    }

    public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        // use URLEncoding to encoding parameters
        // use something like URLEncoding's implementation to add token to URL.
        // URLEncoding can be found here: https://github.com/Alamofire/Alamofire/blob/master/Source/ParameterEncoding.swift#L70
    }
}

This does require that token stays the same I guess. Another way could have it pull out token from passed parameters and then treat the token parameter in a special way. Mind you all of this code has not been tested in any way.

As @bjarkehs suggests, writing a custom ParameterEncoding could be a great idea. However, I might prefer searching for a token parameter to in parameters to url encode and then encode the rest as JSON--as opposed to taking the token as an initializer argument. However, that's a personal style and use case decision.

Another option would be to create a PluginType to do this. It would work similarly to AccessTokenPlugin so take a look here for docs and here for implementation.

I'll close this issue since it's been inactive for a while. Feel free to reopen if you still have questions, @kristiyandobrev

@pedrovereza I just came to a similar issue again, so I consider re-openning the issue again, since I am uncertain if it's possible to make for example POST or PUT request to api/resource/:id and pass a json body out of the box, without any additional implementation, as it was suggested above.

@kristiyandobrev I think what you're looking for is what @scottrhoyt suggested:

I might prefer searching for a token parameter to in parameters to url encode and then encode the rest as JSON.

Basically, if you have an endpoint like:

enum API {
    case example(id:String, name:String, lastName: String)
}

In order to make a request to api/resource/:id passing name and lastName as json body, you have to:

  • Encode id on the url
    public var path: String {
        switch self {
        case .example(let id, _, _):
            return "api/resource/\(id)"
        }
    }
  • Pass name and lastName as parameters
    public var parameters: [String : Any]? {
        switch self {
        case .example(let _, let name, let lastName):
            return ["name": name, "lastName": lastName]
        }
    }
  • Encode parameters as json
    public var parameterEncoding: Moya.ParameterEncoding { 
        switch self {
        case .example:
            return JSONEncoding.default 
        }
   }

@kristiyandobrev were you able to make the request as you wanted?

Yes. Thanks for the help .

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

Can you please help with the comment by @AhmAbdallah , it will help many others as well.
I have a similar problem where I need to send parameters in both body and url

@zohairhadi you should be able to do that easier now, with the Task type we have: Task..requestCompositeParameters(bodyParameters:bodyEncoding:urlParameters), which allow you to combine url encoded parameters with another type (data / parameters).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hamada147 picture hamada147  路  3Comments

JianweiWangs picture JianweiWangs  路  3Comments

hjzCy picture hjzCy  路  3Comments

fenixsolorzano picture fenixsolorzano  路  3Comments

cocoatoucher picture cocoatoucher  路  3Comments