前言

有些时候我们会发现方法名称都正确匹配,但就是找不到对应请求接口,所以本文我们来深入了解下何时会出现接口请求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. SpringBoot系列—简单的邮件系统

    1. 效果发送效果图 2. 邮件开发准备工作 3. springboot引入mail服务 4. 启动应用,开始4种邮件发送测试 1. 效果发送效果图 连续发送了四封邮件:普通文本邮件,带附件的邮件,内 ...

  2. 02 . Tomcat多实例并用Nginx反代

    Tomcat虚拟主机 ​ 一个应用程序在某一个端口启动运行产生了一系列的进程就是一个实例,让tomcat启动两个不同的相互独立的进程,产生两个不同的套接字,分别运行在不同的端口,让不同的端口响应不同的 ...

  3. win服务器管理软件巧利用——如何让服务器管理事半功倍

    那些服务器管理大牛估计看到这个标题会笑了,服务器怎么管理,靠自带的远程桌面肯定是远远不够的,要实现上千台服务器同时登陆,没有一个好程序管理,估计得三餐不食为其颠倒. 那么,有什么好的服务器推荐呢?站长 ...

  4. Spring基本介绍

    一:Spring是什么 Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,它以IOC控制反转和AOP面向切面编程为核心,提供了展现层 Spring MVC 和持久层 ...

  5. css背景图片加载失败,页面部分图标无法显示

    1.问题表现:首屏缺失部分图标.点击按钮切换为激活状态时,部分按钮的激活态图标无法显示. 2.问题原因:网络极差,断断续续,点击时添加class:active变为激活态, active.png这张图片 ...

  6. OC语言

    // // main.m // 测试题05 // // Created by yang sanchao on 9/12/15. // Copyright (c) 2015 yang sanchao. ...

  7. LAMP建站简介

    1. LAMP概述 1.1 为什么是LAMP LAMP无非就是Linux+Apache+MySQL+PHP的网站架构体系而已.而之所以叫LAMP,就是取了这几个单词的首字母罢了,但这里的P可以指PHP ...

  8. 小师妹学JavaIO之:用Selector来发好人卡

    目录 简介 Selector介绍 创建Selector 注册Selector到Channel中 SelectionKey selector 和 SelectionKey 总的例子 总结 简介 NIO有 ...

  9. Uint47 calculator【map数组+快速积+各种取余公式】

    Uint47 calculator 题目链接(点击) In the distant space, there is a technologically advanced planet. One day ...

  10. Flask 蓝图(Blueprint)使用方式解析

    Flask蓝图提供了模块化管理程序路由的功能,使程序结构清晰.简单易懂.下面分析蓝图的使用方法 假如说我们要为某所学校的每个人建立一份档案,一个很自然的优化方式就是这些档案如果能分类管理,就是说假如分 ...