使用 Microsoft.HttpClient 和 HttpContentExtensions 的通用 POST 请求

Posted

技术标签:

【中文标题】使用 Microsoft.HttpClient 和 HttpContentExtensions 的通用 POST 请求【英文标题】:Generic POST request using Microsoft.HttpClient and HttpContentExtensions 【发布时间】:2011-01-20 16:46:51 【问题描述】:

我正在使用 WCF REST Starter Kit 中提供的非常棒的 HttpClient。我有以下针对 HelloTxt API 的方法:

public UserValidateResponse Validate()

    HttpClient client = new HttpClient(baseUrl);

    HttpMultipartMimeForm form = new HttpMultipartMimeForm();
    form.Add("app_key", this.AppKey);
    form.Add("user_key", this.UserKey);
    HttpResponseMessage response = client.Post("user.validate", form.CreateHttpContent());

    return response.Content.ReadAsXmlSerializable<UserValidateResponse>();

我有一个很好的通用 GetRequest 方法,如下所示:

public T GetRequest<T>(string query)

    HttpClient client = new HttpClient(baseUrl);
    client.DefaultHeaders.UserAgent.AddString(@"http://www.simply-watches.co.uk/");

    HttpResponseMessage response = client.Get(query);
    response.EnsureStatusIsSuccessful();

    T data = default(T);
    try
    
        data = response.Content.ReadAsXmlSerializable<T>();
        return data;
    
    catch (Exception ex)
    
        Console.Write(String.Format("0: 1", ex.Message, ex.InnerException.Message));
    

    return data;

这样做的好处是您可以按照这个随机示例将 T 作为响应类型传递:

public List<User> GetUsers(int deptid)

    string query = String.Format("department.getUsers?api_key=0&dept_id=1", this.APIKey, deptId);

    return GetRequest<List<User>>(query);

我现在想要使用相同的通用样式 POST 方法,而不是 GET,我确信我可以使用 HttpContentExtensions,但我不知道如何将请求转换为 HttpMultipartMimeForm。这是我目前所拥有的:

public T PostRequest<K, T>(string query, K request)

    HttpClient client = new HttpClient(baseUrl);
    // the following line doesn't work! Any suggestions?
    HttpContent content = HttpContentExtensions.CreateDataContract<K>(request, Encoding.UTF8, "application/x-www-form-urlencoded", typeof(HttpMultipartMimeForm));

    HttpResponseMessage response = client.Post(query, content);
    response.EnsureStatusIsSuccessful();

    T data = default(T);
    try
    
        data = response.Content.ReadAsXmlSerializable<T>();
        return data;
    
    catch (Exception ex)
    
        Console.Write(String.Format("0: 1", ex.Message, ex.InnerException.Message));
    

    return data;

它会被这样调用:

UserValidateResponse response = PostRequest<UserValidateRequest, UserValidateResponse>("user.validate", new UserValidateRequest(this.AppKey, this.UserKey));

它是针对这个 API 工作的:http://hellotxt.com/developers/documentation。非常欢迎任何建议!我可以为每个 POST 定义一个不同的表单,但一般地这样做会很好。

【问题讨论】:

我感觉唯一的办法是使用反射并基于类的简单属性构建一个 HttpMultipartMimeForm,或者序列化对象然后获取根 XML 节点下的第一个子节点. 【参考方案1】:

对此我回答了我自己的问题。该代码可以在我的.NET wrapper for the HelloTxt API - HelloTxt.NET 中看到,并且根据我上面的评论,使用反射来计算请求对象属性,并使用值填充HttpMultipartMimeForm(),同时检查类属性上的Required 数据注释.

有问题的代码是:

/// <summary>
/// Generic post request.
/// </summary>
/// <typeparam name="K">Request Type</typeparam>
/// <typeparam name="T">Response Type</typeparam>
/// <param name="query">e.g. user.validate</param>
/// <param name="request">The Request</param>
/// <returns></returns>
public T PostRequest<K, T>(string query, K request)

    using (var client = GetDefaultClient())
    
        // build form data post
        HttpMultipartMimeForm form = CreateMimeForm<K>(request);

        // call method
        using (HttpResponseMessage response = client.Post(query, form.CreateHttpContent()))
        
            response.EnsureStatusIsSuccessful();
            return response.Content.ReadAsXmlSerializable<T>();
        
    


/// <summary>
/// Builds a HttpMultipartMimeForm from a request object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="request"></param>
/// <returns></returns>
public HttpMultipartMimeForm CreateMimeForm<T>(T request)

    HttpMultipartMimeForm form = new HttpMultipartMimeForm();

    Type type = request.GetType();
    PropertyInfo[] properties = type.GetProperties();
    foreach (PropertyInfo property in properties)
    
        foreach (Attribute attribute in property.GetCustomAttributes(true))
        
            RequiredAttribute requiredAttribute = attribute as RequiredAttribute;
            if (requiredAttribute != null)
            
                if (!requiredAttribute.IsValid(property.GetValue(request, null)))
                
                    //Console.WriteLine("0 [type = 1] [value = 2]", property.Name, property.PropertyType, property.GetValue(property, null));
                    throw new ValidationException(String.Format("0 [type = 1] requires a valid value", property.Name, property.PropertyType));
                
            
        

        if (property.PropertyType == typeof(FileInfo))
        
            FileInfo fi = (FileInfo)property.GetValue(request, null);
            HttpFormFile file = new HttpFormFile();
            file.Content = HttpContent.Create(fi, "application/octet-stream");
            file.FileName = fi.Name;
            file.Name = "image";

            form.Files.Add(file);
        
        else
        
            form.Add(property.Name, String.Format("0", property.GetValue(request, null)));
        
    

    return form;

【讨论】:

您提供的 github 链接已损坏。

以上是关于使用 Microsoft.HttpClient 和 HttpContentExtensions 的通用 POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 php 和 mysql 使用纬度和经度进行几何搜索

Cocoa - 为啥使用 NSInteger 和 CGFloat 而不是使用 int 和 float,或者总是使用 NSNumber?

HTTPS和SSH方式的区别和使用

学习和使用SVN和GitHub——开篇

CPU使用率和正在使用的内存之间有啥关系?

使用 Express 和 GraphQL 而不是简单地使用 Node.js 和 GraphtQL 有啥好处?