Trochę się zakopałem ostatnio ale na rozgrzewkę mam temat z ostatniej przygody.
Jestem zadziorny i jak wszyscy .Net robią Angular2 z MVC 5, to w tym projekcie che pogodzić Angulara z MVC 4.
Specyfika projektu to:
- /front/* - to Angular2 dostępny tylko dla zweryfikowanych
- !/front/* - MVC wraz z weryfikacją.
Angular oczywiście będzie pracował na własnym zarejestrowanym routingu, MVC też, i rozgraniczenie tego na czystych wpisach do MapRoute działało dobrze jeśli użytkownik wszedł na stronę logowania a następnie korzystał z aplikacji. Jeśli jednak posiadał linka /front/xza, a nie był zweryfikowany otrzymywał 404. Brutalne :) Brakowało małęgo RedirectTo /Login?ReturnUrl=%2ffront%2fxza Brutalne :)
Spróbowałem więc na poziomie rooting odfiltrować.
Klasa do zarejestrowania Constraint
public class ServerRouteConstraint : IRouteConstraint
{
private readonly Func<Uri, bool> _predicate;
public ServerRouteConstraint(Func<Uri, bool> predicate)
{
this._predicate = predicate;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName,
RouteValueDictionary values, RouteDirection routeDirection)
{
if(routeDirection == RouteDirection.IncomingRequest)
return this._predicate(httpContext.Request.Url);
return true;
}
}
I zarejestrowanie
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new
{
serverRoute = new ServerRouteConstraint(url =>!isFront(url))
}
);
routes.MapRoute(
name: "angular",
url: "{*url}",
defaults: new { controller = "Home", action = "FrontIndex" },
constraints: new
{
serverRoute = new ServerRouteConstraint(url => isFront(url))
}
);
}
private static bool isFront(Uri url)
{
return url.PathAndQuery.StartsWith("/front",StringComparison.InvariantCultureIgnoreCase) ;
}
Ale tutaj mamy mały problem, gdyż moment wywołania isFornt nie posiada obiektu Sessji. Z tego powodu specjalnie akcja domyślnie jest przekierowywana pod akcje FrontIndex w kontrolerze Home. Tam jest weryfikacja do której również jest potrzebna sesja, i w zależności od tego albo jest wczytywany Index gdzie pałeczkę przejmuje Angular, albo path jest przekazywany do strony logowania.
[HDAuthorization]
public ActionResult Index()
{
ViewBag.Title = "Use angular and have fun";
return View("Index");
}
public ActionResult FrontIndex()
{
if (HDAuthorization.IsLoginUser())
return Index();
else
{
return RedirectToAction("Login", new
{
ReturnUrl = Request.Path
});
}
}
public ActionResult Login()
{
ViewBag.Title = "Login Page";
return View();
}
[HttpPost]
public ActionResult Login(LoginModel model)
{
var loginReturn = loginManager.Login(model);
if (loginReturn.Authenticated)
{
if (Request.Path.StartsWith("/front/"))
return Redirect(Request.Path);
string requestParam = Request.Params["ReturnUrl"];
if (string.IsNullOrWhiteSpace(requestParam))
return RedirectToAction("Index");
return Redirect(requestParam);
}
ViewBag.LoginError = loginReturn.FailureText;
return View();
}
Oczywiście dla angulara konfiguracja wygląda następująco :
const appRoutes: Routes = [
{ path: 'front/requests', component: AppRequests },
{ path: 'front/request/:id', component: AppRequest } ......
I udało się. Może nie top 100 najładniejszych rozwiązań ale spokojnie wystarcza mi odseparowanie path /front/* dla Angulara a pozostałe niech pożera MVC, natomiast co najważniejsze kiedy user nie ma dostępu do /front/ bo nie jest jeszcze zweryfikowany to dostanie redirect do login wraz z linkiem powrotnym do frontu.
via GIPHY