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)

感谢园友【YoMekennywangjin】的提醒,上述必须显式通过路由来进行参数传递才会成功,如果通过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注意事项以及建议的更多相关文章

  1. Web APi之HttpClient注意事项以及建议(四)

    前言 之前对于用SelfHost来手动实现Web API的宿主模式,似乎不是太深入,所以本篇文章我们一起来讨论关于利用HttpClient来访问Web API上的资源来进行探讨以及注意相关事项,希望此 ...

  2. .net core web api 与httpclient发送和接收文件及数据

    客户端 HttpClient var url = $"https://localhost:44323/api/values/posttest?resource_source=yangwwme ...

  3. Web API: Client: HttpClient Message Handlers

    原文地址: http://www.asp.net/web-api/overview/web-api-clients/httpclient-message-handlers using System; ...

  4. .NET Core Web API使用HttpClient提交文件的二进制流(multipart/form-data内容类型)

    需求背景: 在需要通过服务端请求传递文件二进制文件流数据到相关的服务端保存时,如对接第三方接口很多情况下都会提供一个上传文件的接口,但是当你直接通过前端Ajax的方式将文件流上传到对方提供的接口的时候 ...

  5. .NET MVC和.NET WEB api混用时注意事项

    1.同时配置了mvc路由和api路由时,mvc路由无法访问(调用所有mvc路由全部404错误) 在Global.asax中,需注意路由注册的顺序,将api路由注册放在最后: 即将 void Appli ...

  6. 【ASP.NET Web API2】利用HttpClient调用Web API(TODO)

    参照: 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 纯属记录一下遇到的问题: 我们利用HttpClient来调用自宿主方式寄宿的Web API.HttpCl ...

  7. 杂项:ASP.NET Web API

    ylbtech-杂项:ASP.NET Web API ASP.NET Web API 是一种框架,用于轻松构建可以访问多种客户端(包括浏览器和移动设备)的 HTTP 服务. ASP.NET Web A ...

  8. 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...

  9. 开发笔记:用Owin Host实现脱离IIS跑Web API单元测试

    今天在开发一个ASP.NET Web API项目写单元测试时,实在无法忍受之前的笨方法,决定改过自新. 之前Web API的单元测试需要进行以下的操作: 初始配置: 1)在IIS中创建一个站点指定We ...

随机推荐

  1. 玩转Bootstrap(JS插件篇)-第1章 模态弹出框 :1-3 模态弹出框

    模态弹出框(Modals) 这一小节我们先来讲解一个“模态弹出框”,插件的源文件:modal.js. 右侧代码编辑器(30行)就是单独引入 bootstrap 中发布出的“modal.js”文件. 样 ...

  2. Beginning SDL 2.0(5) 基于MFC和SDL的YuvPlayer

    本文是在“Beginning SDL 2.0(4) YUV加载及渲染”(以下简称BS4)基础上做的功能完善,如果你对之间介绍的内容了解不多,麻烦先阅读之前的内容. 本文主要介绍如何完成一个基于MFC和 ...

  3. 【Deep Learning】Hinton. Reducing the Dimensionality of Data with Neural Networks Reading Note

    2006年,机器学习泰斗.多伦多大学计算机系教授Geoffery Hinton在Science发表文章,提出基于深度信念网络(Deep Belief Networks, DBN)可使用非监督的逐层贪心 ...

  4. celery 实例进阶

    认识 这里有几个概念,task.worker.broker.顾名思义,task 就是老板交给你的各种任务,worker 就是你手下干活的人员. 那什么是 Broker 呢? 老板给你下发任务时,你需要 ...

  5. Spring事务管理要点总结

    # Spring事务管理要点总结 ### 要点---- 事务是企业级应用中必不可缺少的技术,用来确保数据的完整性和一致性.- Spring事务管理并不实现事务管理的实现,而是借助Hibernate\J ...

  6. js之获取url中"?"后面的字串

    url : index.php?id=123 <script type="text/javascript"> function GetRequest() { var u ...

  7. Java后端,应该日常翻看的中文技术网站<转>

    你还在学习吗? 1.内容生产者 InfoQ中文技术第一站,佩服霍老板,真金白银地为中国程序员们生产内容. ImportNew专门面向Java的内容生产者兼聚合者,偶然也有些面向入门的小白文. 并发编程 ...

  8. Android——用PagerAdapter实现View滑动效果

    效果: ViewPage来源于android -support.v4 什么是viewPage?ViewPage 类似于ListView 用于显示多个View集合. 支持页面左右滑动. 如何使用view ...

  9. python json dumps与loads有可能犯的错误

    json.dumps() 是将一个Python数据结构转换为一个JSON编码的字符串 json.loads() 是将一个JSON编码的字符串转换为一个Python数据结构     如下: >&g ...

  10. Redis的内存回收机制

    Redis的内存回收机制 2018年01月16日 17:11:48 chs007chs 阅读数:1172   Redis的内存回收机制主要体现在一下两个方面: 删除过期时间的键对象 删除过期键对象 : ...