libcurl实现POST请求

libcurl实现POST请求

需要三个文件,目录结构如下图:

httpclient.c 实现POST请求的封装

httpclient.h 用于对外提供接口

test.c 是测试文件,测试POST能否发送请求。

httpclient.c文件,封装libcurl的POST请求接口,实现POST发送,并获取content数据。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <curl/curl.h>
#include <curl/easy.h>

#include "httpclient.h"


/*
 * post请求的返回结果回调函数
 * 把content的数据copy到userdata给定的空间
 * 注意:需要在外部释放申请的 userdata.data 空间
 */
size_t post_callback(void* contents, size_t size, size_t nmemb, void* userdata) {
    // 判断contents是否返回了空值
    if (contents == NULL) {
        return 0;
    }

    // 一次回调返回的数据量
    size_t realsize = size * nmemb;  
    if (userdata == NULL) {
        return realsize ;
    }

    // 类型转换
    http_resp_data * resp_data = (http_resp_data *) userdata;

    resp_data->data= (char*)calloc(1, realsize );
    if (resp_data->data== NULL) {
        printf("not enough memory (realloc returned NULL)\n");
        return 0;
    }
    memcpy(resp_data->data, contents, realsize);
    resp_data->size = realsize;

    return realsize;  //必须返回真实的数据
}


#define MAX_URL_LEN 256


/*
 * 示例 :
 * proto : http  or   https
 * host : 127.0.0.1:8080   host:port
 * method : /xxx/yyy
 * data : {"aa":"aaaaaaaaaaa","bb","bbbbbbbbbbbbbbbb", .... }
 * size : data的字节数
 * user_data : 回调函数待处理的数据
 */
int http_post_by_json(const char * proto, const char* host, const char* method, const char* data, size_t size, http_resp_data* user_data) {
    int ret = 0;
    // 1、初始化
    CURL* curl = curl_easy_init();
    if (curl == NULL) {
        printf("curl easy init");
        ret = -1;
        goto over;
    }

    // 2、拼写完整的URL
    char new_url[MAX_URL_LEN] = {0} ;
    if (method[0] == '/') {
        sprintf(new_url, "%s://%s%s", proto, host, method );
    } else {
        sprintf(new_url, "%s://%s/%s", proto, host, method);
    }

    // 3、设置参数
    // 3.1、设置使用POST方法(默认使用GET方法发送请求),
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    // 返回完整的HTTP响应头
    curl_easy_setopt(curl, CURLOPT_HEADER, 1);

    // 3.2、header.Content-Type 和 header.Accept 信息
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json") ;
    headers = curl_slist_append(headers, "Accept: */*") ;

    // 3.3、设置 header.Host
    char host_header[MAX_URL_LEN] = {0};
    sprintf(host_header, "Host: %s", host);
    headers = curl_slist_append(headers, host_header);

    // 3.4、设置header和body
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, size);

    // 3.5、设置请求超时时间
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);

    // 3.6、设置回调函数
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, post_callback);

    // 3.7、允许重定向地址
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1) ;

    // 3.8、设置url地址
    curl_easy_setopt(curl, CURLOPT_URL, new_url);

    // 3.9、设置向回调函数传递的数据
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)user_data);

    // 4、发起请求
    CURLcode res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        const char* error = curl_easy_strerror(res);
        printf("http post request failure, url=%s curl code=%d error=%s", new_url, res, error);
        ret = -1;
        goto over;
    } 
    // 5、判断返回码
    long http_code = 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
    if (http_code >= 400) {
        printf("http post request failure, url=%s curl code=%ld", new_url, http_code) ;
        ret = -1;
        goto over;
    } else {
        printf("success http_code=%ld\n", http_code) ;
    }

over :
    curl_easy_cleanup(curl);
    return ret;
}

httpclient.h文件,上方代码对应的头文件,用于对外暴露接口。

#ifndef __HTTP_CLIENT_H__
#define __HTTP_CLIENT_H__
#include <stdio.h>

typedef struct http_resp_data_t {
    char * data;
    size_t size;
} http_resp_data;


/*
 * 示例 :
 * proto : http  or   https
 * host : 127.0.0.1:8080   host:port
 * method : /xxx/yyy
 * data : {"aa":"aaaaaaaaaaa","bb","bbbbbbbbbbbbbbbb", .... }
 * size : data的字节数
 * user_data : 回调函数待处理的数据
 */
int http_post_by_json(const char * proto, const char* host, const char* method, const char* data, size_t size, http_resp_data* user_data) ;

#endif

调用程序test.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#include "httpclient.h"


int main() {
    http_resp_data resp_data ;
    memset(&resp_data, 0x00, sizeof(http_resp_data)) ;
    char * data = "{\"name\":\"madbull.site\", \"tel\":\"12312312311\", \"email\":\"aaa@ccc.com\"}" ;    
    http_post_by_json("http", "127.0.0.1:3322", "/test/t1", data, strlen(data), &resp_data) ;
    if(resp_data.data != NULL) {
        printf("resp_data.data [%s]\n", resp_data.data) ;
        free(resp_data.data) ;
        memset(&resp_data, 0x00, sizeof(http_resp_data)) ;
    }

    return 0 ;
}

本次测试用来 之前使用go搭建的 POST请求,可以翻看 https://www.madbull.site/?p=749 搭建起http服务器。服务正常启动后,可按照下边的方法编译和测试:

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注