.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 ...
随机推荐
- Vue拖拽交换数据(非插件)
HelloWorld.vue 文件 <template> <div class="hello"> <h1>{{ msg }}</h1> ...
- python3程序开发指南——第1章 笔记
python文件的扩展名为.py ,但是python GUI程序的扩展名为.pyw 在python中,注释以#开始,作用范围为该行 IDLE提供了三个关键功能:输入python表达式与代码,并在pyt ...
- 「MoreThanJava」一文了解二进制和CPU工作原理
「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...
- 【Hadoop】hdfs的秘密,namenode,datanode,yarn,安全模式,fsimage,edits...
1.bin/hdfs namenode -format ** 注意事项 1.在配置好了配置文件之后,首次启动之前,做初始化操作 2.在后续启动的时候,不需要再初始化 3.初始化的一些影响 一.初始化操 ...
- Java实现 蓝桥杯 历届试题 危险系数
问题描述 抗日战争时期,冀中平原的地道战曾发挥重要作用. 地道的多个站点间有通道连接,形成了庞大的网络.但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系. 我们来定义一个危险系数DF( ...
- tab-switch 样式的添加 与 tab元素样式的切换
要点: 1.多个div类名相同情况下添加class样式 2.siblings() 方法返回被选元素的所有同级元素.DOM 树:该方法沿着 DOM 元素的同级元素向前和向后遍历. 3.利用索引,只添加当 ...
- 没有国产主机,怎么开发:交叉编译和QEMU虚拟机
1. 背景 近期国产化的趋势越来越浓,包括国产操作系统.国产CPU等.时隔十多年,QQ for Linux也更新了.做为软件开发人员,"有幸"也需要适配国产化.至于国产化的意义等就 ...
- v-model 指令来实现双向数据绑定
<div id="app"> <p>{{ message }}</p> <input v-model="message" ...
- Firefox中input元素,不能重新获取焦点函数focus()
js校验输入框的函数: function is_number(feild) { var strRegExp = /^\d+(\.\d{1,2})?$/; if (!strRegExp.test(fei ...
- MongoDB 基础知识学习笔记
注意:本文假设您已经安装好 MongoDB 数据库并启动它了. 连接 MongoDB.数据库操作.集合操作 连接 MongoDB mongo ip:port/dbName -u username -p ...