7

My middleware class is in different class library project and controller is in different project. What I am trying to do, if specific condition does not meet then redirect to the Custom Controller/Action method from middleware.

However, I am not able to do that with Response.Redirect method.

How can I do this in middleware class?

2
  • 1
    What is the condition? Is it authentication/authorization, or something else? If it's auth, you probably want to return 401 or 403 (depending on whether it's authentication or authorization that fails), and if it's something else you probably don't want to do it in a middleware in the first place. Could you be more specific? Commented Apr 19, 2016 at 7:28
  • @TomasLycken Yes, it is not related authentication/authorization, it is for if specific client/tenant is not found then redirect to some controllers action method to show error message. To get client name from httpcontext the logic is written in middleware Commented Apr 19, 2016 at 8:25

4 Answers 4

8

Here is middleware that examines the request and redirects. It works with either inline middleware or with a middleware 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();
}

Here is the middleware 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);
        }
    }
}

See Docs » Fundamentals » Middleware for more info.

Sign up to request clarification or add additional context in comments.

2 Comments

It seems you have provided the Configure method of Startup class ? How can I return it from middleware, if error occurs there ?
That app.Use is middleware. :-) I have added another way of doing it.
7

It seems you're using a middleware for the wrong reasons.

I recommend that you either have the middleware return a (very minimal) 404 by simply writing it to the response stream (instead of forwarding to Next()), or don't do this in a middleware at all but instead in a globally registered IActionFilter in your MVC app.


I've explained the rationale for the above advice in the comments, but I think it's important enough to lift into the actual answer:

In a middleware pipeline, you want each component to be as independent as possible. A couple of things enable this loose coupling in OWIN:

  • The input to, and output from, each component has the same format, whether there are 10 other middleware components before it, or none at all

  • The convention is that each part of the pipeline can do one or more of three things, in this order:

    1. Read (and modify) the incoming request.

    2. Decide to handle the request entirely, or forward handling to the next component.

    3. Write to the response stream.

When sticking to these conventions, it becomes very easy to compose, decompose and re-compose pipelines from reusable middleware components. (Want request logging? Just hook up a middleware component at the start of the pipe. Want some general authentication logic across the board? Add a component in the auth stage of the pipe. Want to switch to a different logging framework? Replace the logging component. Want to apply the same logging across an ecosystem of microservices? Re-use the component. Etcetera, ad infinum...) This works so well, because the components both stay within their boundaries, and work with a contract that the web server itself can understand.

ASP.NET WebAPI might seem to be a different beast, but in reality it's just another OWIN component which is always configured to handle the request, and never to forward to a next component (and thus they've made it difficult to register a component after WebApi in the pipeline...).

What you're trying to do, breaks the contract of the second point there - you want to tell the next component how to handle the request. But that's not up to you - it's up to the next component.

8 Comments

Could you please explain why his use case is inappropriate for middleware.
@ShaunLuttin A middleware should, generally, not care what the next part of the pipeline does - it should just (maybe) do something to the request, (maybe) forward the context to the next part of the pipeline, and (maybe) do something to the response. If you need to know something about what happens down the line, you're trying to do it too early.
How does that relate to the OPs use case?
@ShaunLuttin: Based on some properties of the incoming request, the OP wants to decide how a later part of the pipeline should handle the request. What I'm saying is that no middleware should ever do that - it should just decide whether to forward the request or not. Every other decision should be left to the next part of the pipeline.
That makes perfect sense. Thank you for the explanation.
|
3

You can use "Request Editing Middleware" to change the nature of the request as it moves down the pipeline. This fits in with the general "best practices" and will be more efficient than sending a redirect notice back to the browser.

This example redirects to an action method than can return an image:

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);
    }

}

And create an action method that takes a query and can return a file:

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;
    }
}

If you use a redirect on response, it will cause the user's browser to receive a redirect notice and then send a new request to your application, rather than consuming the original request and spitting out the file right away.

2 Comments

This looks like a good idea, however I can't seem to get it working. I added my custom middleware after the app.UseSession() and before the last app.UseEndpoints middleware. I Console.WriteLine the context.Request.Path before and after editing it, and it looks like the property was correctly modified. But the request is not being redirected to where I set it to go. My guess is the terminal app.UseEndpoints middleware does something to it or ignores it somehow. Any ideas?
Hi, @nick. I use code similar to your example in my project, but with the transition to net 6, the controller method has ceased to be called. And you don't know how to fix it?
-1

Redirect to different action without an redirect result (302)

You can Redirect a request to a different action without there being an intermediate redirect request the following way.

First you have the middleware look like this. Here we just edit the URL.

CustomRedirectMiddleware.cs

public class CustomRedirectMiddleware(RequestDelegate next)
{
    private readonly RequestDelegate _next = next;


    public async Task InvokeAsync(HttpContext context)
    {
        context.Request.Path = "/Home/Index";
        await _next(context);
    }
}

This can however only work before calling app.UseRouting since the routing middleware fills the IEndpoint feature and its request delegate.

Program.cs

 app.UseRouting();
 app.UseMiddleware<CustomRedirectMiddleware>();

Access Endpoint metadata in RedirectMiddleware

If for whatever reason you need any metadata from the endpoint before you can determine to redirect you must change the startup to this.

Program.cs

 app.UseRouting();
 app.UseMiddleware<CustomRedirectMiddleware>();
 app.UseRouting();

This does by itself however not work. You also have set contexts.Endpoint to null, otherwise the second call to app.UseRouting will just use the results of the first one which will be a request delegate to the old URL.

CustomRedirectMiddleware.cs

 public class CustomRedirectMiddleware(RequestDelegate next)
 {
     private readonly RequestDelegate _next = next;


     public async Task InvokeAsync(HttpContext context)
     {
         // Retrieve the endpoint metadata from the current request.

         var endpointFeature = context.Features.Get<IEndpointFeature>();
         var endpoint = endpointFeature?.Endpoint;

         // ignore static files
         if (endpoint == null || endpoint.Metadata.GetMetadata<ShouldRedirectAttribute>() == null) 
         {
             await _next(context);
             return;
         }

         context.SetEndpoint(null); /* set it to null to make sure the second UseRouting middleware does its work */

         // Rewrite the request path to /Home/Index
         context.Request.Path = "/Home/Index";
         await _next(context);
     }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.