Laden...

Routing Probleme in Portable Areas

Erstellt von b0b0nr1 vor 11 Jahren Letzter Beitrag vor 11 Jahren 1.529 Views
B
b0b0nr1 Themenstarter:in
92 Beiträge seit 2011
vor 11 Jahren
Routing Probleme in Portable Areas

Hi!

Ich stehe vor folgendem Problem:

Wenn ich die URL News/Show/<Topicname> Aufrufe funktioniert alles. Möchte man jedoch in den Kommentaren die Seite (von 1 zu 2 zb) wechseln kommt bei News/Show/<Topicname>/1 eine 404 Seite.

Ich weiß leider nicht wo der Wurm drin ist.

Anbei das Script zum Registrieren der PortableArea:


public override void RegisterArea(System.Web.Mvc.AreaRegistrationContext context, IApplicationBus bus)
        {
            context.MapRoute(
                "News_Default",
                 AreaName +"{controller}/{action}/{id}/{category}/{subcategory}",
                new { controller = "News", action = "Archive", id=UrlParameter.Optional, category =  UrlParameter.Optional, subcategory=UrlParameter.Optional }

                );
            context.MapRoute(
              "News_Show",
               AreaName + "{controller}/{action}/{id}",
              new { controller = "News", action = "Show", id = UrlParameter.Optional},
              new string[] { "MvcContrib.PortableAreas" }

              );
            context.MapRoute(
              "News_Show_Paging",
               AreaName +"{controller}/{action}/{id}/{page}",
              new { controller = "News", action = "Show", id = UrlParameter.Optional, page = UrlParameter.Optional},
              new string[] { "MvcContrib.PortableAreas" }

              );
            RegisterAreaEmbeddedResources();
        }

Und so ist im news controller die Funktion Show aufgebaut:




public ActionResult Show(string id, int page = 1) {
//.....
ViewBag.P = page;
return View();
}


Warum erkennt er int page nicht?

Ich finde leider den Fehler nicht 😦

16.806 Beiträge seit 2008
vor 11 Jahren

Die Reihenfolge stimmt nicht - und auch die Logik nicht.

News_Show_Paging muss vor News_Show sein; ansonsten würde er die News_Show_Paging Route nie erreichen.
Und bei News_Show_Paging kann die ID gar nicht optional sein, da a) die Route nicht matchen würde und b) die page gar nicht zugeordnet werden könnte.

Das gleiche Spiel mit News_Default.
Du hast hier fast alles optional, dass die Route wahrscheinlich falsch matched und Du so niemals an die eigentliche Route kommen kannst.

Du musst drauf achten, dass die Routen in der korrekten Reigenfolge in der Table definiert wurden - die erste Route, die passt, wird nämlich genommen.
Zudem solltest Du nicht (nur) nach dem Topicname gehen - kann je nach Gerät nämlich falsch an den Server zurück gesendet werden (Encoding etc) und damit wäre die Route ebenfalls niemals zu erreichen. Angesagt wäre also eine Mischung aus Topicname und ID, wie in etwa /News/3713700-Routing_Probleme_in_Portable_Areas um das mal Beispielhaft an diesem Thread hier zu zeigen.

Suchen kann man dann den Thread sehr einfach und schnell anhand der ID - das Topic wird hierbei nicht in die Suche einbezogen; es dient nur zur besseren Ansicht des Users.
Weiterer Vorteil: Durch einen Topicchange (News Update) wird der Link nicht ungültig (fast als Permlink zu bezeichnen).

B
b0b0nr1 Themenstarter:in
92 Beiträge seit 2011
vor 11 Jahren

Hi, danke für die schnelle Antwort.

Ich durchsuche grad einige MVC Tutorials....

Ich habe jetzt erstmal alle Routings in der PortableArea auskommentiert um nur News_Show zu testen mit Seitenzahlen.

Folgende Varianten hatte ich aus einen Tutorials (Codeproject glaub ich wars)

Leider immer noch 404:



       context.MapRoute(
              "News_Show_Paging",
               AreaName +"{controller}/{action}/{id}/{page}",
              new { controller = "News", action = "Show"}

              );

  • ohne id und page genauer zu definieren



       context.MapRoute(
              "News_Show_Paging",
               AreaName +"{controller}/{action}/{id}/{page}",
              new { controller = "News", action = "Show", id="", page=0}

              );



  • ebenfalls kein Erfolg



       context.MapRoute(
              "News_Show_Paging",
               AreaName +"{controller}/{action}/{id}/{page}",
              new { controller = "News", action = "Show", id="*", page="*"}

              );




Ich denke ich brauch im Sachen Routing ein genaues Tutorial oder sowas. Ich bin mir nicht mal sicher, ob eine Variante richtig war 😦

16.806 Beiträge seit 2008
vor 11 Jahren

Schaut nicht so aus, als ob Du überhaupt ansatzweise verstehst, was Du da tust 😉
Was soll id = "" und was soll id ="*"? Sinnfrei getestet oder irgendwo gelesen?

In der MSDn gibts nen riesen Artikel über das Routing - Du solltest Dir aber erst mal Gedanken machen, wie der Workflow des Users aussieht.
Zudem solltest Du den Workflow dann in den Actions hierarchisch umsetzen und anschließend die Routen definieren.

Bedenke: die erste Route die passt, wird genommen.

URL: /News/1/5

Korrekt:
Route1: /News/{id}/{page}
Route2: /News/{id}

Falsch:
Route1: /News/{id}
Route1: /News/{id}/{page}

B
b0b0nr1 Themenstarter:in
92 Beiträge seit 2011
vor 11 Jahren

sorry für die späte rückmeldung 😉

Meintest du diesen Article? blogs.msdn.com/b/dparys/archive/2010/09/13/routen_2d00_in_2d00_aspnet_2d00_mvc_2d00_definieren.aspx

da es eine Portable Area ist sehe ich auf MVCcontrib -> Docu, dass ich den AreaName vorne dran hängen muss.

Ich habe daher jetzt folgendes probiert:
EDIT: Kleinen Denkfehler gefunden. Route hieß immer noch /{page}, habe ich geändert, geht leider trotzdem noch nicht 😦


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using nCMSNET.ViewEngine;
using MvcContrib.PortableAreas;
using MvcContrib;
namespace nCMS_NET.News
{
    public class NewsRegistration:PortableAreaRegistration
    {   
        public override void RegisterArea(System.Web.Mvc.AreaRegistrationContext context, IApplicationBus bus)
        {
            /*context.MapRoute(
                "News_Default",
                 AreaName +"{controller}/{action}/{id}/{category}/{subcategory}",
                new { controller = "News", action = "Archive", id=UrlParameter.Optional, category =  UrlParameter.Optional, subcategory=UrlParameter.Optional }

                );
          */
             context.MapRoute(
              "News_Show_Paging",
               AreaName +"{controller}/{action}/{id}/{p}",
              new { controller = "News", action = "Show", id="", p=""}

              );

            /*
            context.MapRoute(
            "News_Show",
             AreaName + "{controller}/{action}/{id}",
            new { controller = "News", action = "Show", id = "" },
            new string[] { "MvcContrib.PortableAreas" }

            );*/
            RegisterAreaEmbeddedResources();
        }

        public override string AreaName
        {
            get { return "News"; }
        }
    }
}

Funktioniert bisher leider immer noch nicht.

Was mich wundert...es ist nicht die ASP.net 404 Seite.... ich glaub die vom IIS:

http://beta.networkfreaks.de/News/Show/TESTBEITRAG_2/2

Wie gesagt ohne die page gehts, obwohl ich nur eine Route drinnen habe:

http://beta.networkfreaks.de/News/Show/TESTBEITRAG_2

Hier der Code der kompletten Action des Controllers:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using nCMS_NET.nModules;
using Module.News.Models;
using nCMS_NET.CORE.SqlHelper;
using System.Data.SqlClient;
using nCMS_NET;
using nCMS_NET.Pagination;
using nCMS_NET.MemberShip;
using nCMS_NET.CORE.Permissions;
using nCMS_NET.CORE.Captcha;
using nCMS_NET.CORE.GlobalSettings;
namespace Module.News
{
    public class NewsController : Controller
    {

  
       


       
        [HttpGet]
        public ActionResult Show(string id="", string p ="") {

            int page = 0;
            int.TryParse(p, out page);

            if (String.IsNullOrEmpty(id)) throw new Exception("ID IS NULL!");
            SqlHelper Sql = new SqlHelper();
            Sql.SysConnect();
            if (page < 1) page = 1;
            SqlParameter[] P = new SqlParameter[] { new SqlParameter("@seo", id) };
            
            SqlDataReader R = Sql.SysReader("SELECT id FROM "+ new nCMS_NET.CORE.ThisApplication.ThisApplication().getSqlPrefix +"Content WHERE SEOname=@seo", P);
            List<nCMS_NET.Content.nContent> Entry = new List<nCMS_NET.Content.nContent>();
            List<nCMS_NET.Content.nContent> Comments = new List<nCMS_NET.Content.nContent>();
            
            if (!R.HasRows) return RedirectToActionPermanent("Code/404", "Error");
            else {

                R.Read();
                int idd = ((int)R["id"]);
                nCMS_NET.Content.ContentManagement.GetContent G = new nCMS_NET.Content.ContentManagement.GetContent(idd);
                nCMS_NET.Content.ContentManagement.GetContent C = new nCMS_NET.Content.ContentManagement.GetContent(new string[] { "eNewsComment" },null,null,"{prefix}Content.cDate","DESC",false,page,10,idd);
                                    
                Entry = G.getList();
                Comments = C.getList();
                if (Entry.Count == 0) RedirectToActionPermanent("Code/404", "Error");
                Entry[0].GenerateLink();
                if (Entry[0].ContentType != "eNews") RedirectToActionPermanent(Entry[0].TargetAction, Entry[0].TargetController);
                nPagingCollection pages = new nPagingCollection(C.TotalRows, 10);
                ViewData["TotalRows"] = C.TotalRows;
                ViewData["Show"] = Comments.Count;
                ViewData["Pages"] = pages;
                ViewData["Page"] = page;
            }

            R.Close();
            Sql.SysDisconnect();
            ViewData["Entry"] = Entry[0];
            ViewData["Comments"] = Comments;
           
            return View();
        }


        //
        // GET: /News/
        public ActionResult Index()
        {



            return RedirectToAction("Archive");
        }
        public ActionResult Archive(int id=1, string category = "", string subcategory = "")
        {



            int page = id;
            if (id < 1) page = 1;



            nCMS_NET.CORE.GlobalSettings.GlobalSettings G = new nCMS_NET.CORE.GlobalSettings.GlobalSettings();
            int max = 0;
            if (String.IsNullOrEmpty(G.Read("GLOBAL_NEWS_MAX_ENTRIES")))
            {

                G.Write("GLOBAL_NEWS_MAX_ENTRIES", "10");

            }
            max = Convert.ToInt32(G.Read("GLOBAL_NEWS_MAX_ENTRIES"));




            nCMS_NET.Content.ContentManagement.GetContent News = new nCMS_NET.Content.ContentManagement.GetContent(new string[] { 
    "eNews"
    
    }, null, null, "{prefix}Content.cDate", "DESC", false, page, max);


            List<nCMS_NET.Content.nContent> N = News.getList();
            nPagingCollection pages = new nPagingCollection(News.TotalRows, max);
            ViewData["TotalRows"] = News.TotalRows;
            ViewData["News"] = N;
            ViewData["Show"] = N.Count;
            ViewData["Pages"] = pages;
            ViewData["Page"] = page;

     
 
 
            return View();
}
    }
}



Ich hoffe ich stell mich gerade nich allzu unklug an, aber dieses Routing möchte irgendwie nicht in meinen Kopf 😕

16.806 Beiträge seit 2008
vor 11 Jahren

Naja, dass das AreaName hier weiter überschrieben wird, bezweifel ich.

Daher wird er "wohl" auf die Route NewsShow statt News/Show reagieren.
Oder Du musst das ganze als

	  context.MapRoute(
	   "News_Show_Paging",
		AreaName +"/News/Show/{id}/{p}",
	   new { controller = "News", action = "Show", id="", p=""}
	  );

statt

	  context.MapRoute(
	   "News_Show_Paging",
		AreaName +"/News/Show/{id}/{p}",
	   new { controller = "News", action = "Show", id="", p=""}
	  );

definieren.
Jedenfalls würde man das ganze so umsetzen, ohne PortableAreas und ich kann mir nicht vorstellen, dass MVCContrib das hier herzaubert.
Trotzdem macht es bei fixen Routen überhaupt keinen sinn, platzhalter zu verwenden!

Ohne die Page wirds wahrscheinlich funktionieren, weil zufälligerweise die Standard Route von MVC matched.
Würdest Du aber selbst wissen, wenn Du das getan hättest, was in Parys Blog steht: Url Tester verwenden!

Aber ich stufe es immer noch so ein, dass Du gar nicht weißt, was Du da überhaupt definierst!

Ich würde das so definieren

  public override void RegisterArea(System.Web.Mvc.AreaRegistrationContext context, IApplicationBus bus)
 {
	// Bedenke hierbei: wenn eine PageNumber angegeben wird, aber keine newsID, dann wird die pageNumber als NewsID interpretiert....
 
	  context.MapRoute(
	   "News_Show",
		AreaName +"/News/{newsID}-{newsTitle}/{pageNumber}",
	   new { controller = "News", action = "Show", newsID= UrlParameter.Optional, pageNumber = UrlParameter.Optional, newsTitle = UrlParameter.Optional}
	  );
	  
	  // ODER !!!

	 context.MapRoute(
	   "News_Show_News_Page",
		AreaName +"/News/{newsID}-{newsTitle}/{pageNumber}",
	   new { controller = "News", action = "ShowWithPage", newsTitle = UrlParameter.Optional}
	  );
	  
	  
	 context.MapRoute(
	   "News_Show_News",
		AreaName +"/News/{newsID}-{newsTitle}",
	   new { controller = "News", action = "Show", newsTitle = UrlParameter.Optional}
	  );
	  
	  
	context.MapRoute(
	   "News_Show_All",
		AreaName +"/News",
	   new { controller = "News", action = "Index"}
	  );
	  
	  // Hier wären dann aber auch 3 getrennte Actions notwendig
	  // ist aber auch übersichtlicher!
	  // Dieses würde aber auch dem SEO Gedanken folgen
	  
	  
	  
	  
	context.MapRoute(
	 "News_Default",
	  AreaName +"/{controller}/{action}",
	 new { controller = "News" }
	);
 }

und so handeln:


 // Wie gesagt - niemals nach Newstiteln suchen! Viel zu anfällig!
 // Page ist immer noch eine Nummer, kein String
  public ActionResult Show(int? newsID, int? pageNumber)
  {
	if( newsID == null )
	{
		// Alle News anzeigen
	}
	else if( p == null )
	{
		// Erste Seite anzeigen
		
	}else
	{
		// Spezifische Seite anzeigen, sofern sie existiert
	}
 }

Und lager die Business-Logik für das Auslesen der News aus!
Du solltest im Controller eher Dinge nutzen wie
GetNewsById(id) und in ner andren DLL die Suche nach der jeweiligen News umsetzen.

Und nutze ViewModels!
ViewBag und ViewData sind typenlos und damit unschön!

Wo man einen Default gar nicht braucht, wenn man alles richtig gemacht hat.

Wenn Du kein ErrorHandling und keine eigene 404 Seite definiert hast ist es normal, dass der IIS seine Standard 404 ausliefert.

B
b0b0nr1 Themenstarter:in
92 Beiträge seit 2011
vor 11 Jahren

Erledigt -

sorry für die späte Rückmeldung.

Deine Routingbeispiele waren perfekt und dank den letzten VS Stunden (Vernetzte System - Switch Routing Tabellen 😉 ) in der BS kam mir heut erst die zündene Idee.

Und die Lösung war einfach^^

Die "Adressierung" war falsch.

Undzwar dank der Erweiterung RouteDebugger (die ich euch nicht vorenthalten möchte, ist für NuGet Package Manager) kam ich darauf, dass der aufgerufene Namespace nicht stimmt.

Ich hatte im news Controller den namespace "Module.News" doch es wurde probiert den Projektstandartnamespace aufzurufen unter dessen Namen logischer Weise der News Controller nicht zu finden war.

Also für alle die neu im Routing sind wie ich das Toll hier hilft enorm:

http://nuget.org/packages/routedebugger

Kleine Info, das Tutorial auf der Herstellerseite ist veraltet. Einfach installieren wie dort beschrieben und hinterher nicht vergessen die DLL wieder zu löschen wenn ihr fertig seid;) der zeigt die debug info dauerhaft an solang die DLL vorhanden ist.