从中间件调用控制器的动作方法
Call controller's action method from middleware
我的中间件 class 在不同的 class 库项目中,控制器在不同的项目中。我想做的是,如果不满足特定条件,则从中间件重定向到自定义 Controller/Action 方法。
但是,我无法用 Response.Redirect 方法做到这一点。
如何在中间件中执行此操作 class?
感谢任何帮助!
罗希特
看来您使用中间件的原因不对。
我建议您通过简单地将中间件 return 写入响应流(而不是转发到 Next()
)来拥有中间件 return 一个(非常小的)404,或者不要这样做完全在中间件中,而不是在 MVC 应用程序中的全局注册 IActionFilter
中。
我已经在评论中解释了上述建议的基本原理,但我认为重要的是要提出实际答案:
在中间件管道中,您希望每个组件尽可能独立。在 OWIN 中启用这种松散耦合有几件事:
每个组件的输入和输出格式相同,不管它之前是否有10个其他中间件组件,或者根本none
惯例是管道的每个部分都可以按以下顺序执行以下三项操作中的一项或多项:
读取(和修改)传入请求。
决定完全处理请求,还是将处理转发给下一个组件。
写入响应流。
当坚持这些约定时,从可重用的中间件组件组合、分解和重新组合管道变得非常容易。 (想要请求日志记录?只需在管道的开头连接一个中间件组件。想要全面的一些通用身份验证逻辑?在管道的 auth 阶段添加一个组件。想要切换到不同的日志记录框架?替换日志记录组件。想要在微服务生态系统中应用相同的日志记录吗?重复使用该组件。等等,无穷无尽......)这非常有效,因为组件都在它们的边界内,并且与网络的合同一起工作服务器本身可以理解。
ASP.NET WebAPI 可能看起来是一个不同的野兽,但实际上它只是另一个 OWIN 组件,它总是被配置为处理请求,并且永远不会转发到下一个组件(因此他们已经使得在管道中的 WebApi 之后注册组件变得困难...)。
你想做的是,打破了第二点的约定——你想告诉下一个组件如何处理请求。 但这不取决于你 - 这取决于下一个组件。
这是检查请求和重定向的中间件。它适用于内联中间件或中间件 class.
public void Configure(IApplicationBuilder app)
{
// use inline middleware
app.Use(async (context, next) =>
{
// if specific condition does not meet
if (context.Request.Path.ToString().Equals("/foo"))
{
context.Response.Redirect("path/to/controller/action");
}
else
{
await next.Invoke();
}
});
// or use a middleware class
app.UseMiddleware<RedirectMiddleware>();
app.UseMvc();
}
这里是中间件class。
public class RedirectMiddleware
{
private readonly RequestDelegate _next;
public RedirectMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// if specific condition does not meet
if (context.Request.Path.ToString().Equals("/bar"))
{
context.Response.Redirect("path/to/controller/action");
}
else
{
await _next.Invoke(context);
}
}
}
有关详细信息,请参阅 Docs » Fundamentals » Middleware。
您可以使用 "Request Editing Middleware" 更改请求在管道中移动时的性质。这符合常规 "best practices" 并且比将重定向通知发送回浏览器更有效。
此示例比 return 图片重定向到操作方法:
public class ResolveImageMiddleware
{
private readonly RequestDelegate _next;
public ResolveImageMiddleware(RequestDelegate deg)
{
_next = deg;
}
public async Task InvokeAsync(HttpContext context, AppDbContext db)
{
var path = context.Request.Path;
if (path.HasValue && path.Value.StartsWith("/imgs"))
{
context.Request.Path = "/Home/GetImageFromDb";
context.Request.QueryString = new QueryString("?title=" + path.Value.Replace("/imgs/", ""));
}
await _next(context);
}
}
并创建一个接受查询并可以 return 文件的操作方法:
public class HomeController : Controller
{
private EFImageRepo imageRepository;
public HomeController(EFImageRepo repo)
{
imageRepository = repo;
}
public FileResult GetImageFromDb([FromQuery]string title)
{
var img = imageRepository.GetImg(title);
if (img != null)
{
byte[] contents = img.ImageBytes;
return File(contents, "image/jpg");
}
return null;
}
}
如果您在响应中使用重定向,它会导致用户的浏览器收到重定向通知,然后向您的应用程序发送新请求,而不是使用原始请求并立即吐出文件。
我的中间件 class 在不同的 class 库项目中,控制器在不同的项目中。我想做的是,如果不满足特定条件,则从中间件重定向到自定义 Controller/Action 方法。
但是,我无法用 Response.Redirect 方法做到这一点。
如何在中间件中执行此操作 class?
感谢任何帮助!
罗希特
看来您使用中间件的原因不对。
我建议您通过简单地将中间件 return 写入响应流(而不是转发到 Next()
)来拥有中间件 return 一个(非常小的)404,或者不要这样做完全在中间件中,而不是在 MVC 应用程序中的全局注册 IActionFilter
中。
我已经在评论中解释了上述建议的基本原理,但我认为重要的是要提出实际答案:
在中间件管道中,您希望每个组件尽可能独立。在 OWIN 中启用这种松散耦合有几件事:
每个组件的输入和输出格式相同,不管它之前是否有10个其他中间件组件,或者根本none
惯例是管道的每个部分都可以按以下顺序执行以下三项操作中的一项或多项:
读取(和修改)传入请求。
决定完全处理请求,还是将处理转发给下一个组件。
写入响应流。
当坚持这些约定时,从可重用的中间件组件组合、分解和重新组合管道变得非常容易。 (想要请求日志记录?只需在管道的开头连接一个中间件组件。想要全面的一些通用身份验证逻辑?在管道的 auth 阶段添加一个组件。想要切换到不同的日志记录框架?替换日志记录组件。想要在微服务生态系统中应用相同的日志记录吗?重复使用该组件。等等,无穷无尽......)这非常有效,因为组件都在它们的边界内,并且与网络的合同一起工作服务器本身可以理解。
ASP.NET WebAPI 可能看起来是一个不同的野兽,但实际上它只是另一个 OWIN 组件,它总是被配置为处理请求,并且永远不会转发到下一个组件(因此他们已经使得在管道中的 WebApi 之后注册组件变得困难...)。
你想做的是,打破了第二点的约定——你想告诉下一个组件如何处理请求。 但这不取决于你 - 这取决于下一个组件。
这是检查请求和重定向的中间件。它适用于内联中间件或中间件 class.
public void Configure(IApplicationBuilder app)
{
// use inline middleware
app.Use(async (context, next) =>
{
// if specific condition does not meet
if (context.Request.Path.ToString().Equals("/foo"))
{
context.Response.Redirect("path/to/controller/action");
}
else
{
await next.Invoke();
}
});
// or use a middleware class
app.UseMiddleware<RedirectMiddleware>();
app.UseMvc();
}
这里是中间件class。
public class RedirectMiddleware
{
private readonly RequestDelegate _next;
public RedirectMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// if specific condition does not meet
if (context.Request.Path.ToString().Equals("/bar"))
{
context.Response.Redirect("path/to/controller/action");
}
else
{
await _next.Invoke(context);
}
}
}
有关详细信息,请参阅 Docs » Fundamentals » Middleware。
您可以使用 "Request Editing Middleware" 更改请求在管道中移动时的性质。这符合常规 "best practices" 并且比将重定向通知发送回浏览器更有效。
此示例比 return 图片重定向到操作方法:
public class ResolveImageMiddleware
{
private readonly RequestDelegate _next;
public ResolveImageMiddleware(RequestDelegate deg)
{
_next = deg;
}
public async Task InvokeAsync(HttpContext context, AppDbContext db)
{
var path = context.Request.Path;
if (path.HasValue && path.Value.StartsWith("/imgs"))
{
context.Request.Path = "/Home/GetImageFromDb";
context.Request.QueryString = new QueryString("?title=" + path.Value.Replace("/imgs/", ""));
}
await _next(context);
}
}
并创建一个接受查询并可以 return 文件的操作方法:
public class HomeController : Controller
{
private EFImageRepo imageRepository;
public HomeController(EFImageRepo repo)
{
imageRepository = repo;
}
public FileResult GetImageFromDb([FromQuery]string title)
{
var img = imageRepository.GetImg(title);
if (img != null)
{
byte[] contents = img.ImageBytes;
return File(contents, "image/jpg");
}
return null;
}
}
如果您在响应中使用重定向,它会导致用户的浏览器收到重定向通知,然后向您的应用程序发送新请求,而不是使用原始请求并立即吐出文件。