Alamofire请求如何添加httpBody

背景

服务端给的接口,需要将参数以application/json的格式,作为请求体,以post请求的方式传给服务端。
项目的网络请求框架是封装的Alamofire,业务层只需要构建业务对应的Request和Response即可。业务代码大致如下:

class Request: WowRequest<Respone> {
        
        var countInfo: Bool = false
        
        var page: Int = 0
        
        var pageSize: Int = 10

        override var requestPath: String {
            "/community/my" //url的path
        }
        override var requestType: HttpRequestMethod {
            .get //http method
        }
        override var params: [String : Any] {
            //携带的参数
            var p = super.params
            p["countInfo"] = countInfo
            p["page"] = page
            p["pageSize"] = pageSize
            return p
        }
    }

框架代码最终调用Alamofire的网络请求API,我们比较关心的一点是,如何处理params,查看Alamofire的源码

/// - returns: The encoded request.
    public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()

        guard let parameters = parameters else { return urlRequest }

        if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
            guard let url = urlRequest.url else {
                throw AFError.parameterEncodingFailed(reason: .missingURL)
            }

            if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
                let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
                urlComponents.percentEncodedQuery = percentEncodedQuery
                urlRequest.url = urlComponents.url
            }
        } else {
            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
            }
            urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
        }

        return urlRequest
    }

代码中将Get请求的参数直接拼接到url上;Post请求参数转为data,并作为httpbody设置给了request,也就是说所有的附加参数,最终都是通过httpbody传给了服务端,格式为"application/x-www-form-urlencoded; charset=utf-8".那我们的需求是既需要带上参数,同时还要支持携带application/json格式的请求体。

解决方案

解决思路可能就是2条:

  1. 将自定义application/json请求体数据添加到原始的参数处理中,整体作为一个httpBody发到服务端;
  2. 将自定义的application/json请求体单独作为httpbody添加到request中,但是需要额外处理携带的其他Params.
    方案1是行不通的,因为两者的Content-Type不一致,一个是urlencode的表单形式,一个是json形式,二者只能选其一。除非服务端配合修改接口,把自定义请求体的内容,也作为一个参数传过去。
    方案2是行得通的。既然自定义的请求体占用了请求的httpbody字段,那其他参数只能以键值对的方式添加到url上,传到服务端。
    解决代码如下:
private func buildRequestWithHttpBody(url: String, method: HTTPMethod, parameters: Parameters, header: HTTPHeaders, httpBody: Data) -> URLRequest? {
//params格式为[String:Any],需要转为[String:String]
        var stringParameters = [String:String]()
        parameters.forEach { (key: String, value: Any) in
           let strKV =  URLEncoding.default.queryComponents(fromKey: key, value: value)
            stringParameters.merge(strKV) { old, new in
                return new
            }
        }
//使用URLComponents作为url转换中间对象,避免自己写url添加参数,无法考虑周全
        guard var urlComponent = URLComponents(string: url) else { return nil }
        urlComponent.queryItems = stringParameters.map { URLQueryItem(name: $0.key, value: $0.value) }
        guard let url = urlComponent.url, var myURLRequest = try? URLRequest(url: url.absoluteString, method: method, headers: header) else {
            Log.debug("错误的URLRequest")
            return nil
        }
        myURLRequest.httpBody = httpBody
        return myURLRequest
    }

经过测试方案二是行得通的。这里面比较让我诧异的是针对post请求,请求参数直接加到url上和以urlencode表单提交,两者对于服务端来说都是可以的。
查阅了java web相关资料,服务端应该是@RequestParam注解来接收携带参数的,对于拼接到url上的参数和通过表单携带的参数都能解析。代码形式如下:

@PostMapping(value = "/test")
public void test(@RequestParam("id") Integer id,
                 @RequestParam("name") String name,
                 @RequestParam("age") Integer age,
                 @RequestBody String content) {
    log.info("id = {}, name = {}, age = {}", id, name, age);
}
总结

如果需要额外携带Application/json的请求体,那么需要将其他参数拼接到request的url上才行;如果不携带application/json请求体,其他参数可以作为表单添加到request的httpbody上。区别就是要把httpbody的坑位留给更需要的人。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值