前言

有些时候我们会发现方法名称都正确匹配,但就是找不到对应请求接口,所以本文我们来深入了解下何时会出现接口请求404的情况。

匹配控制器Action方法(404)

首先我们创建一个web api应用程序,我们给出如下示例控制器代码

[ApiController]
[Route("[controller]/[action]")]
public class WeatherController : ControllerBase
{
[HttpGet]
string Get()
{
return "Hello World";
}
}

当我们进行如上请求时会发现接口请求不到,这是为何呢?细心的你应该可能发现了,对于请求方法是私有,而不是公共的,当我们加上public就可以请求到了接口

[HttpGet("get")]
public string Get()
{
return "Hello World";
}

匹配控制器Action方法本质

经过如上示例,那么对于Action方法的到底要满足怎样的定义才能够不至于请求不到呢?接下来我们看看源码怎么讲。我们找到DefaultApplicationModelProvider类,在此类中有一个OnProvidersExecuting方法用来构建控制器和Action方法模型,当我们构建完毕所有满足条件的控制器模型后,紧接着势必会遍历控制器模型去获取对应控制器模型下的Action方法,这里只截取获取Action方法片段,源码如下:

foreach (var controllerType in context.ControllerTypes)
{
//获取控制器模型下的Action方法
foreach (var methodInfo in controllerType.AsType().GetMethods())
{
var actionModel = CreateActionModel(controllerType, methodInfo);
if (actionModel == null)
{
continue;
} actionModel.Controller = controllerModel;
controllerModel.Actions.Add(actionModel);
}
}

上述红色标记则是创建Action模型的重点,我们继续往下看到底满足哪些条件才创建Action模型呢?

protected virtual ActionModel CreateActionModel(TypeInfo typeInfo, MethodInfo methodInfo)
{
if (typeInfo == null)
{
throw new ArgumentNullException(nameof(typeInfo));
} if (methodInfo == null)
{
throw new ArgumentNullException(nameof(methodInfo));
} if (!IsAction(typeInfo, methodInfo))
{
return null;
}
......
}

到了这个方法里面,我们找到了如何确定一个方法为Action方法的源头,由于该方法有点长,这里我采用文字叙述来作为判断逻辑,如下:

protected virtual bool IsAction(TypeInfo typeInfo, MethodInfo methodInfo)
{
//如果有属性访问器(无效) //如果有NonAction特性标识无效) //如果重写Equals(Object), GetHashCode()方法(无效) //如果实现Dispose方法(无效) //如果是静态方法(无效) //如果是抽象方法(无效) //如果是构造函数(无效) //如果是泛型方法(无效) //必须为公共方法
return methodInfo.IsPublic;
}

如上是从方法定义的角度来过滤而获取Action方法,除此之外,我们请求方法的名称还可以自定义,比如通过路由、ActionName特性指定,那么这二者是否存在优先级呢?比如如下示例:

[ApiController]
[Route("[controller]/[action]")]
public class WeatherController : ControllerBase
{
[HttpGet]
[ActionName("get1")]
public string get()
{
var routeValue = HttpContext.Request.RouteValues.FirstOrDefault(); return routeValue.Value.ToString();
}
}

我们可以看到此时将以ActionName特性作为方法名称。所以在上述过滤方法定义后开始构建方法模型,在此之后还会再做一步操作,那就是查找该方法是否通过ActionName特性标识,若存在则以ActionName特性标识给定的名称作为请求方法名称,否则以方法定义名称为准,源码如下:

var actionModel = new ActionModel(methodInfo, attributes);

AddRange(actionModel.Filters, attributes.OfType<IFilterMetadata>());

var actionName = attributes.OfType<ActionNameAttribute>().FirstOrDefault();
if (actionName?.Name != null)
{
actionModel.ActionName = actionName.Name;
}
else
{
actionModel.ActionName = methodInfo.Name;
}

还没完,若是将路由特性放到Action方法上,如下,此时请求接口应该是weather/get还是weather/get1呢?

[ApiController]
public class WeatherController : ControllerBase
{
[HttpGet]
[Route("weather/get")]
[ActionName("get1")]
public string get()
{
var routeValue = HttpContext.Request.RouteValues.FirstOrDefault(); return routeValue.Value.ToString();
}
}

此时若我们以weather/get1请求将出现404,还是以路由特性模板给定为准进行请求,但最终会将路由上Action方法名称通过ActionName特性上的名称赋值给Action模型中的ActionName进行覆盖,源码如下,所以上述我们得到的action名称为get1,,当然这么做没有任何实际意义。

public static void AddRouteValues(ControllerActionDescriptor actionDescriptor,ControllerModel controller,ActionModel action)
{
foreach (var kvp in action.RouteValues)
{
if (!actionDescriptor.RouteValues.ContainsKey(kvp.Key))
{
actionDescriptor.RouteValues.Add(kvp.Key, kvp.Value);
}
} if (!actionDescriptor.RouteValues.ContainsKey("action"))
{
actionDescriptor.RouteValues.Add("action", action.ActionName ?? string.Empty);
} if (!actionDescriptor.RouteValues.ContainsKey("controller"))
{
actionDescriptor.RouteValues.Add("controller", controller.ControllerName);
}
}

总结

本文我们只是单独针对查找Action方法名称匹配问题做了进一步的探讨,根据源码分析,对Acion方法名称指定会做3步操作:第一,根据方法定义进行过滤筛选,第二,若方法通过AcionName特性标识则以其所给名称为准,否则以方法名称为准,最终赋值给ActionModel上的ActionName属性,第三,将ActionModel上的ActionName值赋值给路由集合中的键Action。

.NET Core请求控制器Action方法正确匹配,但为何404?的更多相关文章

  1. 找到多个与名为“xxx”的控制器匹配的类型。如果为此请求(“{controller}/{action}/{id}”)提供服务的路由没有指定命名空间以搜索与此请求相匹配的控制器,则会发生这种情况。

    一次在建MVC 项目的进行开发的时候,因为后来想到了一个更好的项目名称,就把 Web项目的名称重命名 改了, 然后 程序集名称,默认命名空间,都改成新的了,刚建立的项目本身也不大,运行起来,总是报 & ...

  2. ASP.NET Core 入门教程 4、ASP.NET Core MVC控制器入门

    一.前言 1.本教程主要内容 ASP.NET Core MVC控制器简介 ASP.NET Core MVC控制器操作简介 ASP.NET Core MVC控制器操作简介返回类型简介 ASP.NET C ...

  3. ASP.NET Core 入门笔记5,ASP.NET Core MVC控制器入门

    摘抄自https://www.cnblogs.com/ken-io/p/aspnet-core-tutorial-mvc-controller-action.html 一.前言 1.本教程主要内容 A ...

  4. JQuery ajax请求struts action实现异步刷新的小实例

    这个样例是用JQuery ajax和struts来做的一个小样例,在这个样例中采用两种方式将java Util中的list转换成支json的格式,第一种是用json-lib.jar这个jar包来转换, ...

  5. 扒一扒asp.net core mvc控制器的寻找流程

    不太会排版,大家将就看吧. asp.net core mvc和asp.net mvc中都有一个比较有意思的而又被大家容易忽略的功能,控制器可以写在非Web程序集中,比如Web程序集:"MyW ...

  6. jQuery ajax请求struts action实现异步刷新

    第一步:导入相关jar包,本样例需导入struts相关jar包,json-lib.jar,gson-2.1.jar可以任意选择,但是这里需要都导入,因为为了做测试,两种jar包的转换方式都用到了. 第 ...

  7. SpringMVCURL请求到Action的映射规则

    SpringMVC学习系列(3) 之 URL请求到Action的映射规则 在系列(2)中我们展示了一个简单的get请求,并返回了一个简单的helloworld页面.本篇我们来学习如何来配置一个acti ...

  8. ASP.NET Web API是如何根据请求选择Action的?[下篇]

    ASP.NET Web API是如何根据请求选择Action的?[下篇] 再<上篇>中我们简单介绍了用于实现Action选择机制的HttpActionSelector,接下来我们来讨论本章 ...

  9. ASP.NET Web API是如何根据请求选择Action的?[上篇]

    ASP.NET Web API是如何根据请求选择Action的?[上篇] Web API的调用请求总是针对定义在某个HttpController中的某个Action方法,请求响应的内容来源于调用目标A ...

随机推荐

  1. 从汉堡加料说起——浅谈C#中的Decorator模式

    相信大家都在都在汉堡店吃过汉堡,有些汉堡店很有特色,推出了汉堡订制服务,即,可以在汉堡中加料,加肉饼,加生菜之类(有点类似我们本地的肥肠粉里面加冒结子).更是让不少吃货大快朵颐,大呼过瘾,加6,7层肉 ...

  2. Windows平台搭建Git服务教程详解

    引言 软件企业的核心就是代码,如何确保代码的安全?如何在团队开发中协同工作?为解决这些问题,我们需要采用相应的管理工具来满足管理的需求.探长从最初的VSS.SVN.TFS到现在的Git存储一路走来,感 ...

  3. ES6-常用四种数组

    1.map 1.1 个人理解 映射 一个对一个 例如:[45,57,138]与[{name:'blue',level:0},{name:'zhangsan',level:99},{name:'lisi ...

  4. 【HIVE】hive的安装与使用教程

    hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能,可以将sql语句转换为MapReduce任务进行运行. 其优点是学习成本低,可以通过 ...

  5. Java实现 蓝桥杯 历届试题 核桃的数量

    历届试题 核桃的数量 时间限制:1.0s 内存限制:256.0MB 问题描述 小张是软件项目经理,他带领3个开发组.工期紧,今天都在加班呢.为鼓舞士气,小张打算给每个组发一袋核桃(据传言能补脑).他的 ...

  6. Java实现 蓝桥杯VIP 算法训练 调和数列

    问题描述 输入一个实数x,求最小的n使得,1/2+1/3+1/4+-+1/(n+1)>=x. 输入的实数x保证大于等于0.01,小于等于5.20,并且恰好有两位小数.你的程序要能够处理多组数据, ...

  7. Java实现蓝桥杯VIP算法训练 二元函数

    试题 算法训练 二元函数 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 令二元函数f(x,y)=ax+by,a和b为整数,求一个表达式S的值. 只有满足以下要求的表达式才是合法的: ...

  8. Java实现蓝桥杯VIP算法训练 预测身高

    试题 算法训练 预测身高 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述: 生理卫生老师在课堂上娓娓道来: 你能看见你未来的样子吗?显然不能.但你能预测自己成年后的身高,有公式: 男 ...

  9. Java实现 LeetCode 222 完全二叉树的节点个数

    222. 完全二叉树的节点个数 给出一个完全二叉树,求出该树的节点个数. 说明: 完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集 ...

  10. Java实现 LeetCode 172 阶乘后的零

    172. 阶乘后的零 给定一个整数 n,返回 n! 结果尾数中零的数量. 示例 1: 输入: 3 输出: 0 解释: 3! = 6, 尾数中没有零. 示例 2: 输入: 5 输出: 1 解释: 5! ...