2016-01-30 2 views
3

У меня возникла ситуация, когда сайту может понадобиться ссылка для перенаправления на определенные контроллеры на основе результатов базы данных.Как динамически выбирать контроллер в MVC Core

Например:

site.com/abcd

потребности вернуть результат из пункта контроллера, который обычно называется как/пункт/просмотр/123

Ключевым моментом здесь является что я не могу жестко закодировать abcd в маршрутизации. И некоторые ссылки могут перейти к контроллеру элементов, другие могут перейти к контроллеру Orders.

Я пробовал полный маршрут до контроллера, а затем загружает требуемый контроллер, но среда не установлена, поэтому она не работает должным образом (она не может найти представления).

+0

Выполняет [RedirectToAction()] (https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.redirecttoaction (v = vs.118) .aspx) делает то, что вы хотеть? – JosephGarrone

+1

Один контроллер может перенаправлять на другие контроллеры или даже просто попросить их отобразить их содержимое, поскольку это было его собственное представление. Автоматическая маршрутизация может работать, но мы не знаем вашей логики за ней –

ответ

2

Вы можете получить любое поведение, которое вы желаете, реализовав IRouter, как в this answer, в том числе основываясь на данных из внешнего источника (например, файла конфигурации или базы данных).

Это гораздо более гибкий, чем универсальный маршрут, поскольку он позволяет вам выбирать контроллер на лету.

public class MyRoute : IRouter 
{ 
    private readonly IRouter innerRouter; 

    public MyRoute(IRouter innerRouter) 
    { 
     if (innerRouter == null) 
      throw new ArgumentNullException("innerRouter"); 
     this.innerRouter = innerRouter; 
    } 

    public async Task RouteAsync(RouteContext context) 
    { 
     var requestPath = context.HttpContext.Request.Path.Value; 

     if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/') 
     { 
      // Trim the leading slash 
      requestPath = requestPath.Substring(1); 
     } 

     if (!requestPath.StartsWith("abcd")) 
     { 
      return; 
     } 

     //Invoke MVC controller/action 
     var oldRouteData = context.RouteData; 
     var newRouteData = new RouteData(oldRouteData); 
     newRouteData.Routers.Add(this.innerRouter); 

     newRouteData.Values["controller"] = "Item"; 
     newRouteData.Values["action"] = "View"; 
     newRouteData.Values["id"] = 123; 

     try 
     { 
      context.RouteData = newRouteData; 
      await this.innerRouter.RouteAsync(context); 
     } 
     finally 
     { 
      // Restore the original values to prevent polluting the route data. 
      if (!context.IsHandled) 
      { 
       context.RouteData = oldRouteData; 
      } 
     } 
    } 

    public VirtualPathData GetVirtualPath(VirtualPathContext context) 
    { 
     VirtualPathData result = null; 

     var values = context.Values; 
     var controller = Convert.ToString(values["controller"]); 
     var action = Convert.ToString(values["action"]); 
     var id = Convert.ToString(values["id"]); 

     if ("Item".Equals(controller) && "View".Equals(action)) 
     { 
      result = new VirtualPathData(this, "abcd?id=" + id); 
      context.IsBound = true; 
     } 

     // IMPORTANT: Always return null if there is no match. 
     // This tells .NET routing to check the next route that is registered. 
     return result; 
    } 
} 

Использование
// Add MVC to the request pipeline. 
app.UseMvc(routes => 
{ 
    routes.Routes.Add(new MyRoute(
     innerRouter: routes.DefaultHandler) 
    ); 

    routes.MapRoute(
     name: "default", 
     template: "{controller=Home}/{action=Index}/{id?}"); 

    // Uncomment the following line to add a route for porting Web API 2 controllers. 
    // routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}"); 
}); 

GetVirtualPath должна отражать то, что RouteAsync делает. RouteAsync преобразует URL в значения маршрута, а GetVirtualPath должен преобразовывать одни и те же данные маршрута обратно в тот же URL.

Самый простой способ сделать это - использовать структуру данных для создания двустороннего сопоставления между этими двумя точками данных (как в связанном ответе), поэтому вам не нужно постоянно менять логику в этих двух методах , Эта структура данных должна кэшироваться и не делать ничего слишком ресурсоемкой, поскольку каждый запрос будет использовать ее для определения того, куда отправлять каждый URL.

В качестве альтернативы вы можете создать отдельный маршрут для каждой вашей отдельной части логики и зарегистрировать их все при запуске приложения. Однако вам необходимо убедиться, что они зарегистрированы в правильном порядке и что каждый маршрут будет соответствовать только правильному набору URL-адресов и правильному набору RouteValues.

ПРИМЕЧАНИЕ: Для сценария, такие как это вы почти никогда не должны использовать RedirectToAction. Имейте в виду, что перенаправление отправит HTTP-запрос 302 в браузер, в котором ему будет предложено найти другое местоположение на вашем сервере. В большинстве случаев это лишние накладные расходы, потому что гораздо эффективнее всего направлять первоначальный запрос на требуемый контроллер.

+0

Спасибо за это! Один вопрос: возврат строки в GetVirtualPath вызывает ошибку сборки. –

+0

Еще один вопрос - был ли способ иметь это только для основного маршрута? Поскольку я не знаю, что/abcd может быть, я только хочу выполнить поиск базы данных, если никакие другие маршруты уже не совпадают с URL-адресом. –

+0

Для вашего первого вопроса - это была ошибка копирования кода с моей стороны. Я исправил ответ. – NightOwl888