.NET Core请求控制器Action方法正确匹配,但为何404?
前言
有些时候我们会发现方法名称都正确匹配,但就是找不到对应请求接口,所以本文我们来深入了解下何时会出现接口请求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?的更多相关文章
- 找到多个与名为“xxx”的控制器匹配的类型。如果为此请求(“{controller}/{action}/{id}”)提供服务的路由没有指定命名空间以搜索与此请求相匹配的控制器,则会发生这种情况。
一次在建MVC 项目的进行开发的时候,因为后来想到了一个更好的项目名称,就把 Web项目的名称重命名 改了, 然后 程序集名称,默认命名空间,都改成新的了,刚建立的项目本身也不大,运行起来,总是报 & ...
- ASP.NET Core 入门教程 4、ASP.NET Core MVC控制器入门
一.前言 1.本教程主要内容 ASP.NET Core MVC控制器简介 ASP.NET Core MVC控制器操作简介 ASP.NET Core MVC控制器操作简介返回类型简介 ASP.NET C ...
- ASP.NET Core 入门笔记5,ASP.NET Core MVC控制器入门
摘抄自https://www.cnblogs.com/ken-io/p/aspnet-core-tutorial-mvc-controller-action.html 一.前言 1.本教程主要内容 A ...
- JQuery ajax请求struts action实现异步刷新的小实例
这个样例是用JQuery ajax和struts来做的一个小样例,在这个样例中采用两种方式将java Util中的list转换成支json的格式,第一种是用json-lib.jar这个jar包来转换, ...
- 扒一扒asp.net core mvc控制器的寻找流程
不太会排版,大家将就看吧. asp.net core mvc和asp.net mvc中都有一个比较有意思的而又被大家容易忽略的功能,控制器可以写在非Web程序集中,比如Web程序集:"MyW ...
- jQuery ajax请求struts action实现异步刷新
第一步:导入相关jar包,本样例需导入struts相关jar包,json-lib.jar,gson-2.1.jar可以任意选择,但是这里需要都导入,因为为了做测试,两种jar包的转换方式都用到了. 第 ...
- SpringMVCURL请求到Action的映射规则
SpringMVC学习系列(3) 之 URL请求到Action的映射规则 在系列(2)中我们展示了一个简单的get请求,并返回了一个简单的helloworld页面.本篇我们来学习如何来配置一个acti ...
- ASP.NET Web API是如何根据请求选择Action的?[下篇]
ASP.NET Web API是如何根据请求选择Action的?[下篇] 再<上篇>中我们简单介绍了用于实现Action选择机制的HttpActionSelector,接下来我们来讨论本章 ...
- ASP.NET Web API是如何根据请求选择Action的?[上篇]
ASP.NET Web API是如何根据请求选择Action的?[上篇] Web API的调用请求总是针对定义在某个HttpController中的某个Action方法,请求响应的内容来源于调用目标A ...
随机推荐
- 【Gradle教程】Gradle 入门
本文为我在学习群内分享时在B站直播分享时的文档,直播间地址 http://live.bilibili.com/22263819 PS:问一下,Linux下有什么好用的会议软件么? 知道的朋友烦请评论告 ...
- SSI PAYLOAD
<pre><!--#exec cmd="ls" --></pre><pre><!--#echo var="DATE_ ...
- 第四篇-用Flutter手撸一个抖音国内版,看看有多炫
前言 这次对布局进行优化,主要包含了首页tabview pageview 以及添加几个按钮的操作过程.主要使用到stack层叠布局,tabpview和pageview,tabview两个页面,一个关注 ...
- Linux题目
1.删除某目录下所有文件,只保留指定文件. 例:假设/abc文件夹下有a1.a2....a10文件,保留a5和a6文件,其他全部删除 [root@localhost abc]# touch a{1.. ...
- 小谢第8问:ui框架的css样式如何更改
目前有三种方法, 1.使用scss,增加样式覆盖,但是此种方法要求css的className需要与框架内的元素相一致,因此修改时候需要特别注意,一个父级的不同就可能修改失败 2.deep穿透,这种方法 ...
- Java实现第八届蓝桥杯外星日历
外星日历 题目描述 某星系深处发现了文明遗迹. 他们的计数也是用十进制. 他们的文明也有日历.日历只有天数,没有年.月的概念. 有趣的是,他们也使用了类似"星期"的概念, 只不过他 ...
- App自动化测试框架学习探索--从零开始设计
App自动化测试框架学习探索--从零开始设计---持续更新中,敬请关注 1 批量执行app自动化测试使用多线程设计思路: 1)并发级别选择用methods 2)采用@Test多线程,数据提供类dp单线 ...
- 详解 Seata Golang 客户端 AT 模式及其使用
源码seata-golang 概述 我们知道 Seata Java Client 的 AT 模式,通过代理数据源,实现了对业务代码无侵入的分布式事务协调机制,将与 Transaction Coor ...
- Redis学习笔记(十八) 集群(下)
复制和故障转移 Redis集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制 的主节点下线时,代替下线主节点继续处理命令请求. ...
- dubbo traceId透传实现日志链路追踪(基于Filter和RpcContext实现)
一.要解决什么问题: 使用elk的过程中发现如下问题: 1.无法准确定位一个请求经过了哪些服务 2.多个请求线程的日志交替打印,不利于查看按时间顺序查看一个请求的日志. 二.期望效果 能够查看一个请求 ...