Skip to content

WebApiClient基础

老九 edited this page Oct 8, 2018 · 43 revisions

本章节为了方便读者的理解,相关例子将使用HttpApiClient静态类来创建http接口的代理类,但在生产环境中,使用HttpApiFactory静态来创建http接口的代理类更合理,也是非常有必要的。

1. GET请求

1.1 Get请求简单例子

public interface IMyWebApi : IHttpApi
{
    // GET http://www.mywebapi.com/webapi/user?account=laojiu
    [HttpGet("http://www.mywebapi.com/webapi/user")]
    ITask<string> GetUserByAccountAsync(string account);
}

var client = HttpApiClient.Create<IMyWebApi>();
var userStr = await client.GetUserByAccountAsync("laojiu");

1.2 使用[HttpHost]特性

如果接口IMyWebApi有多个方法且都指向同一服务器,可以将请求的域名抽出来放到HttpHost特性,接口的代码如下:

[HttpHost("http://www.mywebapi.com/")]
public interface IMyWebApi : IHttpApi
{
    // GET /webapi/user?account=laojiu
    [HttpGet("webapi/user")]
    ITask<string> GetUserByAccountAsync(string account);
}

1.3 响应的json/xml内容转换为强类型模型

1.3.1 隐式转换为强类型模型

当方法的返回数据是UserInfo类型的json或xml文本,且响应的Content-Type为application/json或application/xml值时,方法的原有返回类型ITask就可以声明为ITask。

[HttpHost("http://www.mywebapi.com/")]
public interface IMyWebApi : IHttpApi
{
    // GET /webapi/user?account=laojiu
    [HttpGet("webapi/user")]    
    ITask<UserInfo> GetUserByAccountAsync(string account);
}

1.3.2 显式转换为强类型模型

当方法的返回数据是UserInfo类型的json或xml文本,但响应的Content-Type可能不是期望的application/json或application/xml值时,就需要显式声明[JsonReturn]或[XmlReturn]特性。

[HttpHost("http://www.mywebapi.com/")]
public interface IMyWebApi : IHttpApi
{
    // GET /webapi/user?account=laojiu
    [HttpGet("webapi/user")]  
    [JsonReturn] // 指明使用Json处理返回值为UserInfo类型
    ITask<UserInfo> GetUserByAccountAsync(string account);
}

2.请求URL

2.1 URL的格式

无论是GET还是POST等哪种http请求方法,都遵循如下的URL格式:
{Scheme}://{UserName}:{Password}@{Host}:{Port}{Path}{Query}{Fragment}
例如:http://account:[email protected]/path1/?p1=abc#tag

2.2 动态PATH

某些接口方法将路径的一个分段语意化,比如GET http://www.webapiclient.com/{account},这里不同的{account}代表不同账号下的个人信息,使用{参数名}声明路径,在请求前会自动从参数(或参数模型的同名属性)取值替换。

public interface IMyWebApi : IHttpApi
{
    // GET http://www.webapiclient.com/laojiu
    [HttpGet("http://www.webapiclient.com/{account}"]
    ITask<string> GetUserByAccountAsync(string account);
}

2.3 动态URL

如果请求的URL在运行时才确定,可以将请求Url作为一个参数,使用[Url]特性修饰这个参数并作为第一个参数:

public interface IMyWebApi : IHttpApi
{
    // GET {URL}
    [HttpGet]
    ITask<string> GetUserByAccountAsync([Url] string url);
    
    // GET {URL}?account=laojiu
    [HttpGet]
    ITask<string> GetUserByAccountAsync([Url] string url, string account);
}

2.4 Query参数

2.4.1 多个query参数平铺

// GET /webapi/user?account=laojiu&password=123456
[HttpGet("webapi/user")]
ITask<UserInfo> GetUserAsync(string account, string password);

2.4.2 多个query参数合并到模型

public class LoginInfo
{
    public string Account { get; set; }
    public string Password { get; set; }         
}

// GET /webapi/user?account=laojiu&password=123456
[HttpGet("webapi/user")]
ITask<UserInfo> GetUserAsync(LoginInfo loginInfo);

2.4.3 多个query参数平铺+部分合并到模型

public class LoginInfo
{
    public string Account { get; set; }
    public string Password { get; set; }         
}

// GET /webapi/user?account=laojiu&password=123456&role=admin
[HttpGet("webapi/user")]
ITask<UserInfo> GetUserAsync(LoginInfo loginInfo, string role);

2.4.4 显式声明[PathQuery]特性

// GET /webapi/user?account=laojiu&password=123456&role=admin
[HttpGet("webapi/user")]
ITask<UserInfo> GetUserAsync(
    [PathQuery]LoginInfo loginInfo,
    [PathQuery]string role);

对于没有任何特性修饰的每个参数,都默认被[PathQuery]修饰,表示做为请求路径或请求参数处理,[PathQuery]特性可以设置Encoding、IgnoreWhenNull和DateTimeFormat多个属性。

3.POST/PUT请求

3.1 使用Json或Xml提交

  • 使用[XmlContent]修饰强类型模型参数,表示提交xml
  • 使用[JsonContent]修饰强类型模型参数,表示提交json
// POST webapi/user  
// Body user的json文本
[HttpPost("webapi/user")]
ITask<UserInfo> AddUserWithJsonAsync([JsonContent] UserInfo user);

// PUT webapi/user  
// Body user的xml文本
[HttpPut("/webapi/user")]
ITask<UserInfo> UpdateUserWithXmlAsync([XmlContent] UserInfo user);

如果你的UserInfo有DateTime类型的属性,你可以使用[JsonContent("时间格式")]来修饰接口参数,否则时间格式使用HttpApiConfig.FormatOptions.DateTimeFormate。

3.2 使用x-www-form-urlencoded提交

使用[FormContent]修饰强类型模型参数:

// POST webapi/user  
// Body Account=laojiu&Password=123456
[HttpPost("webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync([FormContent] UserInfo user);

使用[FormField]修饰简单类型参数:

// POST webapi/user  
// Body Account=laojiu&Password=123456&fieldX=xxx
[HttpPost("webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync(
    [FormContent] UserInfo user, 
    [FormField] string fieldX);

3.3 使用multipart/form-data提交

  • 使用[MulitpartContent]修饰强类型模型参数
  • 使用[MulitpartText]修饰简单类型参数
  • 使用MulitpartFile类型作为提交的文件
// POST webapi/user  
[HttpPost("webapi/user")]
ITask<UserInfo> UpdateUserWithMulitpartAsync([MulitpartContent] UserInfo user);

// POST webapi/user  
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithMulitpartAsync(
    [MulitpartContent] UserInfo user, 
    [MulitpartText] string nickName,
    MulitpartFile file);

3.4 使用具体的HttpContent类型提交

如果参数是类型是HttpContent类型的子类,如StringContent、ByteArrayContent、StreamContent、FormUrlEncodedContent等等,则可以直接做为参数,但是必须放在其它参数的前面

// POST webapi/user  
// Body Account=laojiu&Password=123456
[HttpPost("webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync(
    FormUrlEncodedContent user);

// POST webapi/user  
// Body Account=laojiu&Password=123456&age=18
[HttpPost("webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync(
    FormUrlEncodedContent user,
    [FormField] int age);

4. 参数别名或参数模型属性别名

4.1 参数别名

参数account提交到服务器时使用的名称是_name:

public interface IMyWebApi : IHttpApi
{
    // GET http://www.mywebapi.com/webapi/user?_name=laojiu
    [HttpGet("http://www.mywebapi.com/webapi/user")]
    ITask<string> GetUserByAccountAsync(
        [AliasAs("_name")] string account);
}

4.2 参数模型属性别名

属性Account提交到服务器时使用的名称是loginAccount:

public class UserInfo
{
    [AliasAs("loginAccount")]
    public string Account { get; set; }

    public string Password { get; set; }
}

5.特性的范围和优先级

5.1 特性的范围

有些特性比如[Header],可以修饰于接口、方法和参数,使用不同的构造器和修饰于不同的地方产生的含义和结果是有点差别的:

  • 修饰接口时,表示接口下的所有方法在请求前都会添加这个请求头;
  • 修饰方法时,表示此方法在请求前添加这个请求头;
  • 修饰参数时,表示参数的值将做为请求头的值,由调用者动态传入;

5.2 特性的优先级

有些特性比如[AutoReturn]和[JsonReturn],可以修饰于接口和方法,但特性的AllowMultiple为false,如果在接口级生明方法级[AutoReturn],在方法级上声明[JsonReturn],此方法实际生效的是[JsonReturn];再比如[Timeout]特性,如果在接口级声明[Timeout(5000)]在方法级声明[Timeout(10000)],实际生效的是[Timeout(10000)],总结如下:

  • AllowMultiple为false的同一个特性,方法级比接口级优先级高
  • AllowMultiple为false的不同类型的[ReturnAttribute],方法级比接口级优先级高
Clone this wiki locally