Web APi之HttpClient注意事项以及建议
Web APi之HttpClient注意事项以及建议
前言
之前对于用SelfHost来手动实现Web API的宿主模式,似乎不是太深入,所以本篇文章我们一起来讨论关于利用HttpClient来访问Web API上的资源来进行探讨以及注意相关事项,希望此文对你也有收获。
来自XML或Json Content的简单参数
当Web API方法中接受如String、Datetime、Int等参数类型时,但是默认情况下这些方法不会接收来自XML或者JSON Body的参数,那么结果就是导致请求失败。接下来我们来进行演示。
我们取名为Default的Web API控制器,方法以及路由注册如下:
[HttpPost]
public string GetString(string message)
{
return message;
} config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{message}",
defaults: new { message = RouteParameter.Optional }
);
接下来我们用客户端发出POST请求以及数据来请求Web API来并返回该数据,如下:
var client = new HttpClient();
var postUrl = "http://localhost:23133/api/default/GetString";
var task = client.PostAsJsonAsync<string>(postUrl,"Hello World").Result; //将参数进行序列化为JSON并发送到Request中的Body中
通过上述路由以及请求方式的配置理论上来说是能通过URI能访问到资源,但是结果却令你大跌眼镜
由上可得知,参数Message的值Hello World已经传递过来,但是此时请求响应的资源上方法的参数并未接收到且返回404未找到该方法。
上面可能效果不是太明显,我们利用Ajax传递时间来进行请求并返回该数据,方法如下:
public HttpResponseMessage GetDateTime(DateTime time)
{ return Request.CreateResponse<DateTime>(HttpStatusCode.OK, time);
}
前台请求如下(路由配置就此略过):
$("#btn").click(function () {
$.ajax({
type: "get",
url: "http://localhost:23133/api/Default/GetDateTime",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: { "time": new Date("2015","09","22") },
cache: false,
success: function (r) {
console.log(r);
}
});
});
此时响应信息如下:
{"Message":"请求无效。","MessageDetail":"对于“WebApplication2.Controllers.DefaultController”中方法“System.Net.Http.HttpResponseMessage GetDateTime(System.DateTime)”的不可以为 null 的类型“System.DateTime”的参数“time”,参数字典包含一个 null 项。可选参数必须为引用类型、可以为 null 的类型或声明为可选参数。"}
出错原因是对于简单的参数(如String、Int以及DateTime等)当进行请求时不会进行解析其属性而会将其设置为null,所以传递过来的时间为null,而方法的时间参数又不能为空,所以会出现错误。对于这种情况我们使用特性[FromBody]来解决【注意】当在前台传递值为字符串或者数字时则是可以解析的,能够成功传递到方法的参数时,而利用HttpClient进行传递时则会出现404。
鉴于上述,我们将其方法参数进行如下修饰即可成功
public HttpResponseMessage GetDateTime([FromBody] DateTime time)
建议
对于前台进行简单参数进行传递以及和利用HttpClient来进行简单参数的传递出现的情况不同,建议在其参数上加上特性 FromBody ,这样两者皆可避免。
补充
当然有FromBody也就对应的有FromURI,添加此特性就可获得URL上该特性参数对应的值,URI如下:
var postUrl = "http://localhost:23133/api/default/GetString?message='xpy0928'";
被请求的资源如下:
[HttpPost]
public string GetString([FromUri] string message)
{
return message;
}
此时请求时该message对应的值就为xpy0928。
UrlEncoded表单字符串解析
我们在操作表单时肯定会有将表单数据作为字典进行传递的情况,下面我们就来演示这种情况。
如下为Web API需要被请求的方法
[HttpPost]
public string GetString(string cnblogs)
{ return cnblogs; }
接下来我们利用客户端来访问上述该方法:
var formVars = new Dictionary<string, string>();
formVars.Add("cnblogs", "xpy0928");
var content = new FormUrlEncodedContent(formVars); var client = new HttpClient();
var postUrl = "http://localhost:23133/api/default/GetString";
var task = client.PostAsync(postUrl, content).Result; //参数未进行序列化
从上述可以看出理论上是可行的,我们通过URI将表单数据以字典形式进行传递去请求Web API,并获取键cnblogs的值。结果会虐死你,如下:
因为这种对于cnblogs参数值映射到方法上的cnblogs参数上在Web API是行不通的,Web API不会进行映射 。
既然简单的参数来接收不行下面我们尝试利用对象来获得该参数的值就像MVC中提交参数到实体上一样。
首先给出一个类,并且该类中含有cnblogs属性,如下:
public class CnblogsModel
{
public string Cnblogs { get; set; }
}
将其方法及参数修改如下:
[HttpPost]
public string GetString(CnblogsModel model)
{ return model.Cnblogs; }
接下来就是见证奇迹的时刻到了,给力啊AngelaBaby,皇天不负有心人,如下:
【注意】由上可知成功的关键是对象中的字段必须要和POST表单中的键必须相匹配,如果有多个键,那么对象中的字段也要与之对应。
上述的解决方案完全是可行的,但是让人忍俊不禁的是这么做似乎有点太操蛋,接受表单中的参数还得建立对象并与之对应,所以得想更好的出路,既然是针对表单的,难道表单中对于接受这样的字典就没有对应的类来进行处理吗,于是乎,开始找啊找,结果找到了一个 FormDataCollection (表面意思是表单数据集合)。貌似可行,来我们继续将其代码进行改写,如下:
[HttpPost]
public string GetString(FormDataCollection collection)
{ return collection.Get("cnblogs"); }
验证通过,如下:
这样就比第一个解决方案可行多了,只是对于这样的集合类有点让人想不明白,为什么不直接通过索引来获取数据而非得用对于单个用Get或者对于多个字段用GetValues()方法
你是不是就以为这样简洁就是可行的呢?那你可就错了,当我们在ASP.NET上运行Web API我们完全可以通过如下来进行获取表单数据:
var request = HttpContext.Current.Request;
var formKeys = request.Form.AllKeys;
建议
当获取表单数据时建议用 HttpContext.Current.Request ,虽然它仅仅只能在ASP.NET上进行应用而无法在SelfHost上使用,但是对于一些复杂的场景下,比如说你需要获得一些Web API上的Request没有暴露的数据,此时用此种方法将是不可替代的,同时HttpContext无论是在ASP.NET还是MVC或者Web API中都是存在的。所以尽量避免使用FormDataCollection,即使看起来在路由上使用它更简单。
补充(1)
感谢园友【敷衍不起】的疑问,刚开始以为挺简单的,结果就是被打脸了,后经过研究对于复杂类型的数据的传递以及接收,若是具体类,利用FormDataCollection是行不通的,我能想到的解决方案是进行如下操作【期待你更好的解决方案】:
var p = new Person() { Name = "person", Student = new Student() { StuName = "xpy0928" } }; var client = new HttpClient(); var postUrl = new Uri("http://localhost:23133/api/default/GetString"); var task = client.PostAsJsonAsync(postUrl, p).Result; //利用PostAsJsonAsync方法来进行序列化类
在Web API上进行接收,如下:
[HttpPost]
public string GetString(Person p)
{
return p.Name;
}
至于用FormDataCollection接收不行的原因是 FormUrlEncodedContent 的参数键和值必须都是string类型,若类不是具体类直接添加键和值都是字符串则是可行的,但是这种情况似乎不太现实,所以对于具体类还是利用 PostAsJsonAsync 来进行解决。
补充(2)
感谢园友【YoMe和kennywangjin】的提醒,上述必须显式通过路由来进行参数传递才会成功,如果通过Request中body来传递就会出现不解析的情况。
访问QueryString
在命名空间System.Net.Http下有一扩展方法如下:
var requestUri = Request.RequestUri.ParseQueryString();
var paramValue = requestUri["param"];
在大多数场景下使用此方法是比较高效的,但是如果在URI中有许多参数,此时你不得不去显式去解析这些参数。
总结
如上述,Web API对于相关参数的映射是比较简单的,但是在一些不同的情况下,你不得不手动做一些工作来进行处理,同时我们也许意识到了Web API并没有提供任何全局上下文对象,例如Request以及过滤中的Formatters等,都是基于Web API来提供特定的方法来进行操作即方法都是为Web API而量身定制,所以在看到Web API强大的同时也要看到其软肋,这样我们才能更好的对于不同场景下作出不同的处理。
Web APi之HttpClient注意事项以及建议的更多相关文章
- Web APi之HttpClient注意事项以及建议(四)
前言 之前对于用SelfHost来手动实现Web API的宿主模式,似乎不是太深入,所以本篇文章我们一起来讨论关于利用HttpClient来访问Web API上的资源来进行探讨以及注意相关事项,希望此 ...
- .net core web api 与httpclient发送和接收文件及数据
客户端 HttpClient var url = $"https://localhost:44323/api/values/posttest?resource_source=yangwwme ...
- Web API: Client: HttpClient Message Handlers
原文地址: http://www.asp.net/web-api/overview/web-api-clients/httpclient-message-handlers using System; ...
- .NET Core Web API使用HttpClient提交文件的二进制流(multipart/form-data内容类型)
需求背景: 在需要通过服务端请求传递文件二进制文件流数据到相关的服务端保存时,如对接第三方接口很多情况下都会提供一个上传文件的接口,但是当你直接通过前端Ajax的方式将文件流上传到对方提供的接口的时候 ...
- .NET MVC和.NET WEB api混用时注意事项
1.同时配置了mvc路由和api路由时,mvc路由无法访问(调用所有mvc路由全部404错误) 在Global.asax中,需注意路由注册的顺序,将api路由注册放在最后: 即将 void Appli ...
- 【ASP.NET Web API2】利用HttpClient调用Web API(TODO)
参照: 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 纯属记录一下遇到的问题: 我们利用HttpClient来调用自宿主方式寄宿的Web API.HttpCl ...
- 杂项:ASP.NET Web API
ylbtech-杂项:ASP.NET Web API ASP.NET Web API 是一种框架,用于轻松构建可以访问多种客户端(包括浏览器和移动设备)的 HTTP 服务. ASP.NET Web A ...
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
- 开发笔记:用Owin Host实现脱离IIS跑Web API单元测试
今天在开发一个ASP.NET Web API项目写单元测试时,实在无法忍受之前的笨方法,决定改过自新. 之前Web API的单元测试需要进行以下的操作: 初始配置: 1)在IIS中创建一个站点指定We ...
随机推荐
- 多媒体文件格式之AVI
[时间:2016-07] [状态:Open] AVI(Audio Video Interleaved的缩写)是一种RIFF(Resource Interchange File Format的缩写)文件 ...
- Testng生成的测试报告乱码解决办法
Testng生成的测试报告乱码解决办法 2017-06-16 1 问题描述 乱码是程序编码不统一,比如Java源代码是utf-8,编译是gbk,这时会乱码. 代码如下: org.testng.Repo ...
- 【Java】自动类型转换规则
自动类型转换遵循下面的规则: 若参与运算的数据类型不同,则先转换成同一类型,然后进行运算. 转换按数据长度增加的方向进行,以保证精度不降低.例如int型和long型运算时,先把int量转成long型后 ...
- Python(四)之Python流程控制(if、while、for)
Python流程控制 if测试: if 条件测试表达式: 组合条件测试: x and y:与运算 x or y:或运算 not x:非运算 while: break:跳出最内层的循环 continue ...
- Python通过ssh连接服务器并执行命令
[本文出自天外归云的博客园] 脚本示例如下: # coding:utf-8 import time,paramiko,re,StringIO def exec_shell(command): ''' ...
- 中南大学oj:1336: Interesting Calculator(广搜经典题目)
http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1336 There is an interesting calculator. It has 3 r ...
- 基于html5海贼王单页视差滚动特效
分享一款基于html5海贼王单页视差滚动特效是一款流行滑落网页特效代码.效果图如下: 在线预览 源码下载 实现的代码: <div class="top"> < ...
- Linux 下Tomcat服务器响应越来越慢分分析
本文转自自:https://www.cnblogs.com/yangc/p/6859985.html ,感谢给我提供提供思路. 服务器响应慢的分析与解决(Linux服务器) 一.分析思路 1.排除本机 ...
- base64 数据编码原理
例子 macOS 终端输入:echo d3d3LmNuYmxvZ3MuY29tL3poZW5nYmlu | base64 -D Linux 终端输入:echo d3d3LmNuYmxvZ3MuY29t ...
- C语言 · 数的运算
算法提高 数的运算 时间限制:1.0s 内存限制:512.0MB 输入一个正整数(范围[1..10000]),打印其平方(不保留小数位).平方根.倒数.(用指针实现,保留2位小数,输 ...