20

मैं एमवीसी 6 के साथ काम करने के लिए this sample RouteBase implementation को परिवर्तित करने का प्रयास कर रहा हूं। मैंने the example in the Routing project का पालन करके इसे अधिकतर काम किया है, लेकिन मैं वापस कैसे लौटने के लिए आगे बढ़ रहा हूं विधि से एसिंक्रोनस Task। मुझे वास्तव में परवाह नहीं है कि यह वास्तव में असीमित है (किसी को भी उत्साहित करता है जो उस उत्तर को प्रदान कर सकता है), क्योंकि अब मैं इसे काम करना चाहता हूं।एएसपी.NET 5 (vNext) में एक कस्टम आईरौटर को लागू करना एमवीसी 6

मेरे पास आउटगोइंग मार्ग कार्यरत हैं (जिसका मतलब है कि ActionLink मार्ग मानों में डालते समय ठीक काम करता है)। समस्या RouteAsync विधि के साथ है।

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

    // Get the page that matches. 
    var page = GetPageList() 
     .Where(x => x.VirtualPath.Equals(requestPath)) 
     .FirstOrDefault(); 

    // If we got back a null value set, that means the URI did not match 
    if (page != null) 
    { 
     var routeData = new RouteData(); 

     // This doesn't work 
     //var routeData = new RouteData(context.RouteData); 

     // This doesn't work 
     //routeData.Routers.Add(this); 

     // This doesn't work 
     //routeData.Routers.Add(new MvcRouteHandler()); 

     // TODO: You might want to use the page object (from the database) to 
     // get both the controller and action, and possibly even an area. 
     // Alternatively, you could create a route for each table and hard-code 
     // this information. 
     routeData.Values["controller"] = "CustomPage"; 
     routeData.Values["action"] = "Details"; 

     // This will be the primary key of the database row. 
     // It might be an integer or a GUID. 
     routeData.Values["id"] = page.Id; 

     context.RouteData = routeData; 

     // When there is a match, the code executes to here 
     context.IsHandled = true; 

     // This test works 
     //await context.HttpContext.Response.WriteAsync("Hello there"); 

     // This doesn't work 
     //return Task.FromResult(routeData); 

     // This doesn't work 
     //return Task.FromResult(context); 
    } 

    // This satisfies the return statement, but 
    // I'm not sure it is the right thing to return. 
    return Task.FromResult(0); 
} 

पूरी विधि एक मैच होने पर अंत तक सभी तरह से चलती है। लेकिन जब इसे निष्पादित किया जाता है, तो यह CustomPage नियंत्रक की Details विधि को कॉल नहीं करता है, जैसा कि इसे करना चाहिए। मुझे बस ब्राउज़र में एक खाली सफेद पृष्ठ मिलता है।

मैं के रूप में this post में किया गया था और यह खाली पृष्ठ पर Hello there लिखते हैं WriteAsync लाइन जोड़ा है, लेकिन मैं नहीं समझ सकता क्यों MVC मेरी नियंत्रक बुला नहीं है (पिछले संस्करणों इस बिना किसी बाधा के काम में)। दुर्भाग्यवश, उस पोस्ट को IRouter या INamedRouter को कार्यान्वित करने के तरीके को छोड़कर रूटिंग के प्रत्येक भाग को शामिल किया गया।

मैं RouteAsync विधि फ़ंक्शन कैसे बना सकता हूं?

पूरे CustomRoute कार्यान्वयन

using Microsoft.AspNet.Routing; 
using Microsoft.Framework.Caching.Memory; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 

public class PageInfo 
{ 
    // VirtualPath should not have a leading slash 
    // example: events/conventions/mycon 
    public string VirtualPath { get; set; } 
    public int Id { get; set; } 
} 

public interface ICustomRoute : IRouter 
{ } 


public class CustomRoute : ICustomRoute 
{ 
    private readonly IMemoryCache cache; 
    private object synclock = new object(); 

    public CustomRoute(IMemoryCache cache) 
    { 
     this.cache = cache; 
    } 

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

     // Get the page that matches. 
     var page = GetPageList() 
      .Where(x => x.VirtualPath.Equals(requestPath)) 
      .FirstOrDefault(); 

     // If we got back a null value set, that means the URI did not match 
     if (page != null) 
     { 
      var routeData = new RouteData(); 

      // TODO: You might want to use the page object (from the database) to 
      // get both the controller and action, and possibly even an area. 
      // Alternatively, you could create a route for each table and hard-code 
      // this information. 
      routeData.Values["controller"] = "CustomPage"; 
      routeData.Values["action"] = "Details"; 

      // This will be the primary key of the database row. 
      // It might be an integer or a GUID. 
      routeData.Values["id"] = page.Id; 

      context.RouteData = routeData; 
      context.IsHandled = true; 
     } 

     return Task.FromResult(0); 
    } 

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

     // Get all of the pages from the cache. 
     var pages = GetPageList(); 

     if (TryFindMatch(pages, context.Values, out page)) 
     { 
      result = new VirtualPathData(this, page.VirtualPath); 
      context.IsBound = true; 
     } 

     return result; 
    } 

    private bool TryFindMatch(IEnumerable<PageInfo> pages, IDictionary<string, object> values, out PageInfo page) 
    { 
     page = null; 
     int id; 
     object idObj; 
     object controller; 
     object action; 

     if (!values.TryGetValue("id", out idObj)) 
     { 
      return false; 
     } 

     id = Convert.ToInt32(idObj); 
     values.TryGetValue("controller", out controller); 
     values.TryGetValue("action", out action); 

     // The logic here should be the inverse of the logic in 
     // GetRouteData(). So, we match the same controller, action, and id. 
     // If we had additional route values there, we would take them all 
     // into consideration during this step. 
     if (action.Equals("Details") && controller.Equals("CustomPage")) 
     { 
      page = pages 
       .Where(x => x.Id.Equals(id)) 
       .FirstOrDefault(); 
      if (page != null) 
      { 
       return true; 
      } 
     } 
     return false; 
    } 

    private IEnumerable<PageInfo> GetPageList() 
    { 
     string key = "__CustomPageList"; 
     IEnumerable<PageInfo> pages; 

     // Only allow one thread to poplate the data 
     if (!this.cache.TryGetValue(key, out pages)) 
     { 
      lock (synclock) 
      { 
       if (!this.cache.TryGetValue(key, out pages)) 
       { 
        // TODO: Retrieve the list of PageInfo objects from the database here. 
        pages = new List<PageInfo>() 
        { 
         new PageInfo() { Id = 1, VirtualPath = "somecategory/somesubcategory/content1" }, 
         new PageInfo() { Id = 2, VirtualPath = "somecategory/somesubcategory/content2" }, 
         new PageInfo() { Id = 3, VirtualPath = "somecategory/somesubcategory/content3" } 
        }; 

        this.cache.Set(key, pages, 
         new MemoryCacheEntryOptions() 
         { 
          Priority = CacheItemPriority.NeverRemove, 
          AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15) 
         }); 
       } 
      } 
     } 

     return pages; 
    } 
} 

CustomRoute डि पंजीकरण

services.AddTransient<ICustomRoute, CustomRoute>(); 

MVC मार्ग विन्यास

// Add MVC to the request pipeline. 
app.UseMvc(routes => 
{ 
    routes.Routes.Add(routes.ServiceProvider.GetService<ICustomRoute>()); 

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

मामले में यह मायने रखती है मैं Beta 5, DNX 4.5.1 और DNX Core 5 उपयोग कर रहा हूँ।

समाधान

मैं एक सामान्य समाधान है कि जानकारी मैं यहाँ सीखा के आधार पर यूआरएल 2 तरह मानचित्रण in this answer के लिए एक सरल प्राथमिक कुंजी के लिए इस्तेमाल किया जा सकता बनाया। प्राथमिक कुंजी के नियंत्रक, क्रिया, डेटा प्रदाता, और डेटाटाइप को एमवीसी 6 रूटिंग में तारों के दौरान निर्दिष्ट किया जा सकता है।

उत्तर

7

@opiants के रूप में, समस्या यह है कि आप अपने RouteAsync विधि में कुछ भी नहीं कर रहे हैं।

डिफ़ॉल्ट रूप से MVC एक आंतरिक लक्ष्य IRouter के साथ एक TemplateRoute उपयोग करता है:

आपका इरादा एक नियंत्रक कार्रवाई विधि बुला खत्म करने के लिए है, तो आप डिफ़ॉल्ट MVC मार्गों से निम्नलिखित दृष्टिकोण इस्तेमाल कर सकते हैं। रूटएसिंक में, टेम्पलेट रूट आंतरिक आईरौटर को प्रतिनिधि करेगा। यह आंतरिक राउटर डिफ़ॉल्ट रूप से MvcRouteHandler के रूप में सेट किया जा रहा है। आपके मामले में, अपने भीतर के लक्ष्य के रूप में एक IRouter जोड़कर शुरू:

public class CustomRoute : ICustomRoute 
{ 
    private readonly IMemoryCache cache; 
    private readonly IRouter target; 
    private object synclock = new object(); 

    public CustomRoute(IMemoryCache cache, IRouter target) 
    { 
     this.cache = cache; 
     this.target = target; 
    } 

फिर MvcRouteHandler उस लक्ष्य है, जो पहले से ही routes.DefaultHandler के रूप में स्थापित किया गया है स्थापित करने के लिए अपने स्टार्टअप अद्यतन:

app.UseMvc(routes => 
{ 
    routes.Routes.Add(
     new CustomRoute(routes.ServiceProvider.GetRequiredService<IMemoryCache>(), 
         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?}"); 
}); 

अंत में, आंतरिक IRouter पर कॉल करने के लिए अपनी AsyncRoute विधि को अपडेट करें, जो MvcRouteHandler होगा। आप गाइड के रूप में TemplateRoute में उस विधि के कार्यान्वयन का उपयोग कर सकते हैं। मैं जल्दी से इस दृष्टिकोण का इस्तेमाल किया और अपने विधि संशोधित रूप में निम्न प्रकार है:

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

    // Get the page that matches. 
    var page = GetPageList() 
     .Where(x => x.VirtualPath.Equals(requestPath)) 
     .FirstOrDefault(); 

    // If we got back a null value set, that means the URI did not match 
    if (page == null) 
    { 
     return; 
    } 


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

    // TODO: You might want to use the page object (from the database) to 
    // get both the controller and action, and possibly even an area. 
    // Alternatively, you could create a route for each table and hard-code 
    // this information. 
    newRouteData.Values["controller"] = "CustomPage"; 
    newRouteData.Values["action"] = "Details"; 

    // This will be the primary key of the database row. 
    // It might be an integer or a GUID. 
    newRouteData.Values["id"] = page.Id; 

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

अद्यतन RC2

TemplateRoute की तरह लग रहा RC2 aspnet रूटिंग में अब चारों ओर है।

मैंने इतिहास की जांच की, और इसका नाम बदलकर RouteBasecommit 36180ab में एक बड़े रिफैक्टरिंग के हिस्से के रूप में रखा गया।

+0

हाँ मैंने एक आंतरिक आईरौटर होने के बारे में सोचा था, लेकिन मुझे नहीं लगता कि आपको इसकी आवश्यकता है। संदर्भ स्थापित करना। इसे झूठ बोलने और जल्दी लौटने से यह अगले पंजीकृत आईरौटर में स्थानांतरित हो जाएगा और आखिरकार मार्गों पर फॉलबैक होगा। डिफॉल्ट हैंडलर (जो एमवीआर रूट हंडलर है) – Dealdiane

+0

क्या आप सुनिश्चित हैं कि कोई रूट नहीं होने पर डिफॉल्ट हैंडलर का उपयोग किया जाएगा ? ऐसा लगता है कि कोड का उपयोग केवल ['MapRoute'] (https://github.com/aspnet/Routing/blob/dev/src/Microsoft.AspNet.Routing/RouteBuilderExtensions.cs) एक्सटेंशन विधि के लिए किया जा रहा है, इसलिए एमवीसी मार्गों को टेम्पलेट रूट का उपयोग एक आंतरिक एमवीसी रूटआउट हैंडलर –

+0

के साथ जोड़ा जाता है ['रूटबिल्डर.बिल्ड'] भी देखें (https://github.com/aspnet/Routing/blob/dev/src/Microsoft.AspNet.Routing/RouteBuilder.cs) जो प्रत्येक परिभाषित मार्गों को जोड़ देगा, लेकिन डिफ़ॉल्ट हैंडलर –

2

प्राथमिक कारण यह क्यों काम नहीं करता है क्योंकि आप RouteAsync विधि में कुछ भी नहीं कर रहे हैं। एक अन्य कारण यह है कि एमवीसी 6 में रूटिंग कैसे काम करती है, यह पिछले एमवीसी रूटिंग के काम से बहुत अलग है, इसलिए शायद source code का उपयोग करके इसे स्क्रैच से लिखना बेहतर होगा क्योंकि इस समय एमवीसी 6 से निपटने वाले बहुत कम लेख हैं ।

संपादित करें: @ डैनियल जेजी। उत्तर इस से अधिक समझ में आता है इसलिए यदि संभव हो तो इसका उपयोग करें। यह किसी और के उपयोग के मामले में फिट हो सकता है, इसलिए मैं इसे यहां छोड़ रहा हूं।

यहाँ एक बहुत ही सरल IRouterbeta7 का उपयोग कर कार्यान्वयन है। यह काम करना चाहिए लेकिन आपको शायद अंतराल को भरने की आवश्यकता होगी।आप page != null नियंत्रकों और कार्यों को हटाने और नीचे दिए गए कोड से बदलने और बदलना भी होगा:

if (page == null) 
{ 
    // Move to next router 
    return; 
} 

// TODO: Replace with correct controller 
var controllerType = typeof(HomeController); 
// TODO: Replace with correct action 
var action = nameof(HomeController.Index); 

// This is used to locate the razor view 
// Remove the trailing "Controller" string 
context.RouteData.Values["Controller"] = controllerType.Name.Substring(0, controllerType.Name.Length - 10); 

var actionInvoker = context.HttpContext.RequestServices.GetRequiredService<IActionInvokerFactory>(); 

var descriptor = new ControllerActionDescriptor 
{ 
    Name = action, 
    MethodInfo = controllerType.GetTypeInfo().DeclaredMethods.Single(m => m.Name == action), 
    ControllerTypeInfo = controllerType.GetTypeInfo(), 
    // Setup filters 
    FilterDescriptors = new List<FilterDescriptor>(), 
    // Setup DI properties 
    BoundProperties = new List<ParameterDescriptor>(0), 
    // Setup action arguments 
    Parameters = new List<ParameterDescriptor>(0), 
    // Setup route constraints 
    RouteConstraints = new List<RouteDataActionConstraint>(0), 
    // This router will work fine without these props set 
    //ControllerName = "Home", 
    //DisplayName = "Home", 
}; 

var accessor = context.HttpContext.RequestServices.GetRequiredService<IActionContextAccessor>(); 

accessor.ActionContext = new ActionContext(context.HttpContext, context.RouteData, descriptor); 

var actionInvokerFactory = context.HttpContext.RequestServices.GetRequiredService<IActionInvokerFactory>(); 
var invoker = actionInvokerFactory.CreateInvoker(accessor.ActionContext); 

// Render the page 
await invoker.InvokeAsync(); 

// Don't execute the next IRouter 
context.IsHandled = true; 

return; 

सुनिश्चित करें कि आप GetRequiredService विस्तार को हल करने Microsoft.Framework.DependencyInjection नाम स्थान के लिए एक संदर्भ जोड़े हैं।

उसके बाद, IRouter रजिस्टर के रूप में प्रति नीचे:

app.UseMvc(routes => 
{ 
    // Run before any default IRouter implementation 
    // or use .Add to run after all the default IRouter implementations 
    routes.Routes.Insert(0, routes.ServiceProvider.GetRequiredService<CustomRoute>()); 

    // .. more code here ... 
}); 

तो बस रजिस्टर है कि आपके आईओसी में,

services.AddSingleton<CustomRoute>(); 

एक और 'क्लीनर' दृष्टिकोण शायद का एक अलग कार्यान्वयन के लिए किया जाएगा IActionSelector

+0

बंद करें, लेकिन कोई सिगार नहीं है। सेट करने के लिए केवल 'IActionContextAccessor' बनाने के बारे में बकवास हटाने के बाद और उसके गुणों में से एक प्राप्त करने के बाद, मैं विधि को सफलतापूर्वक आमंत्रित करने में सक्षम था। हालांकि, अब मुझे एक शून्य संदर्भ अपवाद मिल रहा है। स्टैक ट्रेस की पहली 2 पंक्तियां हैं: 'माइक्रोसॉफ्ट.एस्पनेट.एमवीसीयूआरएल हेल्पर..क्टर (आईएससीओपीएन इंस्टेंस' 1 संदर्भ एक्सेसर, आईएक्शन चयनकर्ता एक्शन चयनकर्ता '' और 'lambda_method (क्लोजर, सर्विसप्रोवाइडर)'। सोचने लगते हैं कि एक बग हो सकता है। मैं बीटा 5 से बीटा 7 में अपग्रेड करने जा रहा हूं यह देखने के लिए कि क्या मैं इसे काम कर सकता हूं और यदि नहीं, तो माइक्रोसॉफ्ट को इसकी रिपोर्ट करें। – NightOwl888

+0

क्षमा करें आपके प्रश्न को ध्यान से नहीं पढ़ा और माना कि आप बीटा 7 पर हैं।उपरोक्त कोड बीटा 7 में परीक्षण किया गया था। बहुत यकीन है कि बीटा पर काम नहीं करेगा 5. इसे मार्गों में जोड़ने के बारे में मेरा संपादन देखें। मार्ग। साथ ही सम्मिलित करें। – Dealdiane

संबंधित मुद्दे