第二节:如何正确使用WebApi和使用过程中的一些坑
一. 基本调用规则
1. 前提
WebApi的默认路由规则为:routeTemplate: "api/{controller}/{id}", 下面为我们统一将它改为 routeTemplate: "api/{controller}/{action}/{id}",这样我们在调用的时候,还是通过拼接方法名来识别,不用考虑上面的坑别的规则了,这里我单纯的来探讨WebApi的传参和调用。
2. 基本的调用规则
是什么请求,在方法上面标注什么特性,常见的有[HttpGet][HttpPost][HttpPut][HttpDelete]。
PS:方法名以Get、Post、Put、Delete开头,WebApi会自动默认这个请求就是Get、Post、Put、Delete请求,而如果你以其他名称开头而又不标注方法的请求方式,那么这个时候服务器虽然找到了这个方法,但是由于请求方式不确定,所以直接返回给你405——方法不被允许的错误。
二. Get请求规则
1. 标准用法:
发起请求的方式都为 api/Second/CheckLogin?userName=admin&pwd=123456 这种类型,不管几个参数(1个或多个),接收的时候,如果是分参数接收,没有任何问题, 可以正常接收,但如果是通过实体类来接收,必须在前面加特性[FromUri],否则接收不到。
PS:当然也可以什么参数都不写,通过传统的Request或者Request.QueryString来接受。
2. 实战测试:
(1). 分别调用CheckLogin1、CheckLogin2、CheckLogin3方法, 发现前两个方法都能正常调用,CheckLogin3这个方法报错, 报:"Message":"出现错误","ExceptionMessage":"未将对象引用设置到对象的实例"。
(2). 调用CheckLogin4方法,报与上面相同的错误,证明dynamic类型也不能这么用。
(3). 调用CheckLogin5方法,能正常接收,表明可以通过传统的Request或者Request.QueryString来接受。
代码分享:
public class LoginModel
{
public string userName { get; set; } public string pwd { get; set; }
}
实体类
#region 01-分参数接收
/// <summary>
/// Get api/Second/CheckLogin1?userName=admin&pwd=123456
/// </summary>
/// <param name="userName"></param>
/// <param name="pwd"></param>
/// <returns></returns>
[HttpGet]
public string CheckLogin1(string userName, string pwd)
{
if (userName == "admin" && pwd == "")
{
return "ok";
}
else
{
return "error";
}
}
#endregion #region 02-用实体接收,加[FromUri]特性
/// <summary>
/// Get api/Second/CheckLogin2?userName=admin&pwd=123456
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpGet]
public string CheckLogin2([FromUri]LoginModel model)
{
if (model.userName == "admin" && model.pwd == "")
{
return "ok";
}
else
{
return "error";
}
}
#endregion #region 03-用实体接收,不加[FromUri]特性
/// <summary>
/// Get api/Second/CheckLogin3?userName=admin&pwd=123456
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpGet]
public string CheckLogin3(LoginModel model)
{
if (model.userName == "admin" && model.pwd == "")
{
return "ok";
}
else
{
return "error";
}
}
#endregion #region 04-用dynamic接收,加[FromUri]特性
/// <summary>
/// Get api/Second/CheckLogin4?userName=admin&pwd=123456
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpGet]
public string CheckLogin4([FromUri]dynamic model)
{
if (model.userName == "admin" && model.pwd == "")
{
return "ok";
}
else
{
return "error";
}
}
#endregion #region 05-没有任何参数,直接用Request相关方法接收
/// <summary>
/// Get api/Second/CheckLogin5?userName=admin&pwd=123456
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpGet]
public string CheckLogin5()
{
var userName = HttpContext.Current.Request["userName"];
var pwd = HttpContext.Current.Request.QueryString["pwd"];
if (userName == "admin" && pwd == "")
{
return "ok";
}
else
{
return "error";
}
}
#endregion
服务器端代码
//一.下面是Get请求的测试
//1. 分参数接收,可以正常访问
$("#getBtn1").click(function () {
$.ajax({ url: "/api/Second/CheckLogin1", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
});
//2. 用实体类接收,前面加[FromUrl],可以正常访问
$("#getBtn2").click(function () {
$.ajax({ url: "/api/Second/CheckLogin2", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
});
//3. 用实体类接收,前面什么不加,报错
// "Message":"出现错误","ExceptionMessage":"未将对象引用设置到对象的实例"
$("#getBtn3").click(function () {
$.ajax({ url: "/api/Second/CheckLogin3", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
});
//4. 用dynamic接收,前面什么不加,报错
// "Message":"出现错误","ExceptionMessage":"未将对象引用设置到对象的实例"
$("#getBtn4").click(function () {
$.ajax({ url: "/api/Second/CheckLogin4", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
});
//5. 后台直接用Request或者Request.QueryString,能正常接收
$("#getBtn5").click(function () {
$.ajax({ url: "/api/Second/CheckLogin5", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
});
前端JS代码
3. 特别说明:
建议忘掉实战测试中的几种错误情况,记住标准用法即可。
三. Post请求规则
1.标准用法:
参数一定要用“实体类”接收(即使一个参数也要封装实体类),客户端既可以用ContentType="application/x-www-form-urlencoded"提交表单,也可以用 ContentType ="application/json"提交, 模型类前面可以加[FromBody],但只能在一个参数前面加.
2.实战测试
(1). 当只有一个参数的情况,且加[FromBody]特性,不封装实体,如Register0,正常的键值对 { userName: "admin" }能访问通,但后台拿不到值,只有省掉键名{ "": "admin" },才能正常访问,且后台能拿到值。
(2). 多个参数的情况,后台分参数接收,如Register1,正常的键值对提交,无法访问,提示找不到匹配的资源。
(3). 一个或多个参数的情况,后台用实体接收,如Register2,且加[FromBody]特性,可以正常访问,并获取到请求值。
①:默认的post请求,即ContentType="application/x-www-form-urlencoded"的形式,可以正常请求。
②:将post请求指定为contentType: 'application/json',且传递的实体格式化成Json字符串,则可以正常请求。
(4). 一个或多个参数的情况,后台用dynamic接收,且加[FromBody]特性,需要分情况讨论。
①:默认的post请求,即ContentType="application/x-www-form-urlencoded"的形式,服务器报500错误。
②:将post请求指定为contentType: 'application/json',且传递的实体格式化成Json字符串,则可以正常请求。
阶段总结:后台用实体接收和用dynamic类型接收,用实体接收,无论是"application/x-www-form-urlencoded"还是"application/json"都能访问;如果用dynamic类型接收,只有"application/json"能访问, 且参数必须是序列化后的字符串
(5). 一个或多个参数的情况,没有参数,如Register4,通过Request或者Request.Form相关方法可以获取请求值。
代码分享:
#region 01-单个参数,加[FromBody]特性
[HttpPost]
public string Register0([FromBody]string userName)
{
if (userName == "admin")
{
return "ok";
}
else
{
return "error";
}
}
#endregion #region 02-多个参数,分参数接收
[HttpPost]
public string Register1(string userName, string pwd)
{
if (userName == "admin" && pwd == "")
{
return "ok";
}
else
{
return "error";
}
}
#endregion #region 03-用实体接收,加[FromBody]特性
[HttpPost]
public string Register2([FromBody]LoginModel model)
{
if (model.userName == "admin" && model.pwd == "")
{
return "ok";
}
else
{
return "error";
}
}
#endregion #region 04-用dynamic接收,加[FromBody]特性
[HttpPost]
public string Register3([FromBody]dynamic model)
{
if (model.userName == "admin" && model.pwd == "")
{
return "ok";
}
else
{
return "error";
}
}
#endregion #region 05-没有任何参数,直接用Request相关方法接收
[HttpPost]
public string Register4()
{
var userName = HttpContext.Current.Request["userName"];
var pwd = HttpContext.Current.Request.Form["pwd"];
if (userName == "admin" && pwd == "")
{
return "ok";
}
else
{
return "error";
}
}
#endregion }
服务器端代码
//二.下面是Post请求的测试(默认情况下为:ContentType="application/x-www-form-urlencoded"提交表单的形式)
//PS: { userName: "admin", pwd: "123456" } 这就是一个JSON对象,也可以叫实体 //1. 一个参数的情况,后台分参数接收,且必须加[FromBody]特性
$("#postBtn0").click(function () {
//1.1 正常拼接,可以访问通,但是拿不到userName的值
$.ajax({ url: "/api/Second/Register0", type: "Post", data: { userName: "admin" }, success: function (data) { alert(data); } });
//1.2 没有键,只有值,可以正常访问,能拿到userName的值
//$.ajax({ url: "/api/Second/Register0", type: "Post", data: { "": "admin" }, success: function (data) { alert(data); } });
});
//2. 多个参数的情况,后台分参数接收,正常的键值对提交,无法访问,找不到匹配的资源
$("#postBtn1").click(function () {
//访问不通
$.ajax({ url: "/api/Second/Register1", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
});
//3. 一个或多个参数的情况,后台用实体接收,且加[FromBody]特性,可以正常访问,并获取到请求值
$("#postBtn2").click(function () {
//情况①,默认的post请求,即ContentType="application/x-www-form-urlencoded"的形式,可以正常请求
//$.ajax({ url: "/api/Second/Register2", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } }); //情况②,将post请求指定为contentType: 'application/json',且传递的参数格式化成Json字符串,则可以正常访问
$.ajax({ url: "/api/Second/Register2", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { alert(data); } }); });
//4. 一个或多个参数的情况,后台用dynamic接收,且加[FromBody]特性,需要分情况讨论
$("#postBtn3").click(function () {
//情况①,默认的post请求,即ContentType="application/x-www-form-urlencoded"的形式,服务器报500错误
//$.ajax({ url: "/api/Second/Register3", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } }); //情况②,将post请求指定为contentType: 'application/json',且传递的参数格式化成Json字符串,则可以正常访问
$.ajax({ url: "/api/Second/Register3", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { alert(data); } });
});
//5. 一个或多个参数的情况,没有参数,通过Request或者Request.Form相关方法可以获取请求值
$("#postBtn4").click(function () {
//访问不通
$.ajax({ url: "/api/Second/Register4", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
});
前端JS代码
四. 总结
Put和Delete请求与Post请求的规则相同,另外还有很多极端的情况,不探讨了,掌握正确的用法,直接去用最佳用法即可。
总结:记住Get请求和Post请求的标准用法以及基本的调用规则,其他坑爹的情况,可以统统忘记了,注意的是ajax的Get请求,加上一个当前时间或者随机数的参数,使用HttpClient 等需要禁用缓存。
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
第二节:如何正确使用WebApi和使用过程中的一些坑的更多相关文章
- 关于webapi练习过程中遇到的一系列问题记录
最近在尝试本地进行webapi调用的过程中,遇到一系列的问题,demo很小但着实让人头疼,先附上demo. 前台页面,目的是展示新闻的分类: 类别模型如下: 控制器代码如下: public Actio ...
- 【精编重制版】JavaWeb 入门级项目实战 -- 文章发布系统 (第二节)
说明 本教程是,原文章发布系统教程的精编重制版,会包含每一节的源码,以及修正之前的一些错误.因为之前的教程只做到了评论模块,很多地方还不完美,因此重制版会修复之前的一些谬误和阐述不清的地方,而且,后期 ...
- delphi 线程教学第二节:在线程时空中操作界面(UI)
第二节:在线程时空中操作界面(UI) 1.为什么要用 TThread ? TThread 基于操作系统的线程函数封装,隐藏了诸多繁琐的细节. 适合于大部分情况多线程任务的实现.这个理由足够了吧 ...
- CUDA:Supercomputing for the Masses (用于大量数据的超级计算)-第二节
原文链接 第二节:第一个内核 Rob Farber 是西北太平洋国家实验室(Pacific Northwest National Laboratory)的高级科研人员.他在多个国家级的实验室进行大型并 ...
- android内部培训视频_第二节 布局基础
第二节:布局入门 一.线性布局 需要掌握的属性: 1.orientation:排列方式 vertical:垂直 Horizontal:水平 2.weight:水平布局的权重 3.gravity:子控件 ...
- 第二节:AppDomain
CLR COM服务器初始化时,会创建一个AppDomain.AppDomain是一组程序集的逻辑容器.CLR初始化时创建的第一个AppDomain称为默认的AppDomain,这个默认的AppDoma ...
- seajs第二节,seajs各模块依赖关系
index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> &l ...
- 学习javascript基础知识系列第二节 - this用法
通过一段代码学习javascript基础知识系列 第二节 - this用法 this是面向对象语言中的一个重要概念,在JAVA,C#等大型语言中,this固定指向运行时的当前对象.但是在javascr ...
- VUE2.0实现购物车和地址选配功能学习第二节
第二节 创建VUE实例 购物车项目计划: 1.创建一个vue实例 2.通过v-for指令渲染产品数据 3.使用filter对金额和图片进行格式化 4.使用v-on实现产品金额动态计算 5.综合演示 ① ...
随机推荐
- Eclipse出错不断,注册表不能乱改
Eclipse打不开,始终报错,还能不能开心的敲代码了??? 首先说下造成我这个愚蠢错误的起源:电脑是win10系统,本来是可以正常使用的.某一天,我正在使用python,打开命令提示符,看见开头是中 ...
- 7 Best Free RAR Password Unlocker Software For Windows
Here is the list of Best Free RAR Password Unlocker Software for Windows. These software run differe ...
- centos7配置静态ip地址
1.配置文件所在目录为 /etc/sysconfig/network-scripts cd /etc/sysconfig/network-scripts 2.打开配置文件进行修改,建议在修改之前先备份 ...
- Linux+Shell常用命令总结
因为自己不经常使用linux的命令行工具,但是mac的终端还是经常使用的,有些命令总是要想一会或者百度一下才知道怎么用,抽时间整理了一下常用的命令,作为笔记. 常用命令 查看文件操作: ls :列出当 ...
- C# FileSystemWatcher 并发
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
- 微信小程序中用户登录和登录态维护
提供用户登录以及维护用户的登录状态,是一个拥有用户系统的软件应用普遍需要做的事情.像微信这样的一个社交平台,如果做一个小程序应用,我们可能很少会去做一个完全脱离和舍弃连接用户信息的纯工具软件. 让用户 ...
- 572. Subtree of Another Tree(easy)
Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and no ...
- centos 6.8 搭建svn服务器
1. yum remove subversion #卸载 svn服务 2.下载svn服务器安装包 yum -y install subversion 3.创建SVN版本库 mkdir -p /opt/ ...
- 使用Node.js搭建数据爬虫crawler
0. 通用爬虫框架包括: (1) 将爬取url加入队列,并获取指定url的前端资源(crawler爬虫框架主要使用Crawler类进行抓取网页) (2)解析前端资源,获取指定所需字段的值,即获取有价值 ...
- red()、redinle()、redlines()三者之间的关系
# 关于read()方法: # 1.读取整个文件,将文件内容放到一个字符串变量中 # 2.如果文件大于可用内存,不可能使用这种处理 file_object = open("a.txt&quo ...