.NET 8.0中的MVC开发

1.DotnetCodeMVC 1.1 启动流程代码 .NET8.0 MVC当中,默认情况下使用如下的代码引导MVC的启动(Intellij Rider的默认模板) using BlazorApp.Controllers; using BlazorAp

1.DotnetCodeMVC

1.1 启动流程代码

.NET8.0 MVC当中,默认情况下使用如下的代码引导MVC的启动(Intellij Rider的默认模板)

using BlazorApp.Controllers;  
using BlazorApp.Services;  
  
var builder = WebApplication.CreateBuilder(args);  
  
// Add services to the container.  
builder.Services.AddControllersWithViews();  
  
var app = builder.Build();  
  
// Configure the HTTP request pipeline.  
if (!app.Environment.IsDevelopment())  
{  
    app.UseExceptionHandler("/Home/Error");  
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.  
  app.UseHsts();  
}  
  
app.UseHttpsRedirection();  
app.UseStaticFiles();  
  
app.UseRouting();  
  
app.UseAuthorization();  
  
app.MapControllerRoute(  
    name: "default",  
    pattern: "{controller=Home}/{action=Index}/{id?}");  
  
app.Run();

介绍一下代码的功能。

下面是通过WebApplication.CreateBuilder创建一个Builder,并构建App的过程,这里的builder.Services.AddControllersWithViews的主要作用是,可以在Controller当中根据View()去找到对应的视图页面。

var builder = WebApplication.CreateBuilder(args);  
  
// Add services to the container.  
builder.Services.AddControllersWithViews();  
  
var app = builder.Build();

然后来看下面的代码,它的作用和SpringMVC当中的HandlerMapping的作用类似,用于根据路径找到对应的Controller方法去处理请求。

app.UseRouting();  

然后来看下面的代码,它的作用是:扫描应用程序中的所有控制器,并自动为每个控制器配置路由映射。它会根据控制器及其方法上的路由属性(例如 [Route]、[HttpGet]、[HttpPost] 等)来设置路由规则,使得请求可以匹配到相应的控制器方法,相当于是启用SpringMVC的注解开发的功能

app.MapControllers();

1.2 指定端口号启动

1.2.1 开发阶段端口号

开发阶段指定端口号,Rider/VSCode进行开发时,可以基于/Properties/launchSettings.json配置文件进行指定端口号。

{  
  "$schema": "http://json.schemastore.org/launchsettings.json",  
  "iisSettings": {  
    "windowsAuthentication": false,  
    "anonymousAuthentication": true,  
    "iisExpress": {  
      "applicationUrl": "http://localhost:50830",  
      "sslPort": 44389  
  }  
  },  
  "profiles": {  
    "http": {  
      "commandName": "Project",  
      "dotnetRunMessages": true,  
      "launchBrowser": true,  
      "applicationUrl": "http://localhost:5144",  
      "environmentVariables": {  
        "ASPNETCORE_ENVIRONMENT": "Development"  
  }  
    },  
    "https": {  
      "commandName": "Project",  
      "dotnetRunMessages": true,  
      "launchBrowser": true,  
      "applicationUrl": "https://localhost:7250;http://localhost:5144",  
      "environmentVariables": {  
        "ASPNETCORE_ENVIRONMENT": "Development"  
  }  
    },  
    "IIS Express": {  
      "commandName": "IISExpress",  
      "launchBrowser": true,  
      "environmentVariables": {  
        "ASPNETCORE_ENVIRONMENT": "Development"  
  }  
    }  
  }  
}

可以配置不同的profile(比如上面的http/https),不同的profile使用不同的端口号。

1.2.2 生产阶段端口号

生产阶段可以基于指定启动时指定命令行参数配置的方式去指定运行端口号。

dotnet run --urls "http://*:5001"
dotnet MyApp.dll --urls "http://*:5000"

2. Dotnet Core MVC当中的Controller路由配置

DotnetCore MVC的实现,和SpringMVC的实现,基本上是一模一样的,基本上没有什么本质上的区别。

2.1 默认约定(按照Controller的名字进行路由映射)

DotnetCoreMVC当中默认的HelloController的实现如下,继承Controller父类。通过构造器的方式,注入了Logger对象。

public class HomeController : Controller  
{  
    private readonly ILogger<HomeController> _logger;  
  
    public HomeController(ILogger<HomeController> logger)  
    {  
        _logger = logger;  
    }  
  
    public IActionResult Index()  
    {  
        return View();  
    }  
  
    public IActionResult Privacy()  
    {  
        return View();  
    }  
  
    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]  
    public IActionResult Error()  
    {  
        return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });  
    }  
}

HelloController当中定义了两个方法IndexPrivacy,意味着可以通过/Hello/Index/Hello/Privacy可以访问到对应的页面。

IndexPrivacy方法当中,根据View()会自动定位到对应的视图文件,默认的视图文件的位置是/Views/{ControllerName}/{ViewName}.cshtml。也就是说

  • HelloController类的Index方法当中调用View()将会自动找到/Views/Hello/Index.cshtml这个视图文件,给前端进行返回。
  • HelloController类的Privacy方法当中调用View()将会自动找到/Views/Hello/Privacy.cshtml这个视图文件,给前端进行返回。

需要注意的是,DotnetCore当中Controller的实现,会为某个请求分配一个新的Controller实例对象,用来进行请求的处理

2.2 覆盖默认约定自定义路由配置

想要覆盖默认行为,先确保在Program.cs当中已经配置了MapControllers配置,如下:

app.UseRouting();
app.MapControllers();

接着,通过[Route]属性(注解)去定义类似如下的路由配置,就可以进入自定义路由配置,可以通过访问/api/Custom/Query进行访问,这里的[controller]是个占位符代表自动填充当前的Controller的名称。

[Route("/api/[controller]")]  
public class CustomController : Controller  
{  
    [HttpGet]  
    [Route("Query")]  
    public IActionResult Query()  
    {  
        return Ok("OK");  
    }  
}

这里的[controller]占位符还可以替换成为[action],代表自动注入方法名称,比如下面的例子当中,我们访问Query方法,但是因为我们路由配置了[action]会被自动替换成为的方法名称,也就是Query,此时我们需要通过/api/Query/Query的方式进行访问。

[Route("/api/[action]")]  
public class CustomController : Controller  
{  
    [HttpGet]  
    [Route("Query")]  
    public IActionResult Query()  
    {  
        return Ok("OK");  
    }  
}

2.3 给前端返回Json数据

定义如下的Controller,通过/api/Custom/Query接口访问就可以发现得到Json数据。

[Route("/api/[controller]")]  
public class CustomController : Controller  
{  
    [HttpGet]  
    [Route("Query")]  
    public IActionResult Query()  
    {  
        var dictionary = new Dictionary<string, string>();  
        dictionary.Add("code", "200");  
        dictionary.Add("message", "OK");  
        return Ok(dictionary);  
    }  
}

Dotnet的MVC架构当中,如果通过Ok返回一个对象,那么就会被默认渲染成为Json数据。

2.4 怎么获取请求当中的参数信息

最直接的方式,可以在Controller方法当中,通过HttpContext.Request去获取到请求参数信息,基于Request可以进行各种参数的获取,包括Query,Body,Headers等参数信息。

[Route("/api/[controller]")]  
public class CustomController : Controller  
{  
    [HttpGet]  
    [Route("Query")]  
    public IActionResult Query()  
    {  
        var request = HttpContext.Request;  
        var queryCollection = HttpContext.Request.Query;  
        var requestBody = HttpContext.Request.Body;  
        var headerDictionary = HttpContext.Request.Headers;  
          
        return Ok("OK");  
    }  
}

也可以基于属性(注解)进行参数的访问访问,主要支持下面的这些属性。

查询参数 使用 [FromQuery] 或 HttpContext.Request.Query 访问。

路由参数 使用 [FromRoute] 或 HttpContext.Request.RouteValues 访问。

表单数据 使用 [FromForm] 或 HttpContext.Request.Form 访问。

请求头 使用 HttpContext.Request.Headers 访问。

请求体 使用 [FromBody] 或 HttpContext.Request.Body 读取。

[Route("/api/[controller]")]  
public class CustomController : Controller  
{  
    [HttpGet]  
    [Route("Query")]  
    public IActionResult Query([FromQuery] string id, [FromQuery] string name)  
    {  
        return Ok($"{id}, {name}");  
    }  
}

通过http://localhost:5144/api/Custom/Query?id=1&name=wanna访问,页面上就可以获取到传递的id和name参数信息。

3.依赖管理与依赖注入

DonetCoreMVC当中基于IOC容器去管理全部的依赖,因此会涉及到添加到容器当中,以及从容器当中进行获取等操作。

3.1 如何将Bean配置到容器当中

我们自定义一个IHelloService接口,定义一个HelloService作为具体的实现

public interface IHelloService  
{  
    public string test()  
    {  
        return "test";  
    }  
}

public class HelloService : IHelloService  
{  

}

接着需要在启动时,将HelloService添加到容器当中,第一个泛型是接口名称,第二个参数是实现类名称。

builder.Services.AddSingleton<IHelloService, HelloService>();

3.2 字段注入

我们尝试进行依赖注入,可以基于如下的方式,添加[FromServices]的方式进行属性的注入,这其实就是对应的Spring当中的@Autowired注入。(如果基于属性注入的方式,那么需要保证属性是public的,private的不行)

[FromServices] public IHelloService HelloService { get; set; }

3.3 构造器注入(推荐)

我们也可以基于构造器的方式进行依赖注入,这也是官方推荐的依赖注入的方式。

private IHelloService HelloService;  
  
public HomeController(ILogger<HomeController> logger, IHelloService helloService)  
{  
    _logger = logger;  
    this.HelloService = helloService;  
}

3.3 方法参数自动注入

当然也可以在某个方法的参数当中直接进行依赖注入,这是SpringMVC当中没有的功能,当然整体是很类似的。比如参考如下:

public IActionResult Index([FromServices] IHelloService helloService2)  
{  
    return View();  
}

3.4 容器当中有多个相同类型的Bean的情况的依赖注入

如果容器当中存在有多个相同类型的Bean的话,可以使用IEnumerable<T>进行注入。

例如我们定义如下的IHelloService的单例Bean,但是IHelloService存在有多个实现。

builder.Services.AddSingleton<IHelloService, HelloService>();  
builder.Services.AddSingleton<IHelloService, HelloService2>();

那么我们注入时,可以使用如下的方式进行注入。

private IEnumerable<IHelloService> HelloServices;  
  
public HomeController(ILogger<HomeController> logger, IEnumerable<IHelloService> helloServices)  
{  
    _logger = logger;  
    this.HelloServices = helloServices;  
}
Comment