Routing for Single Page Application in ASP.NET Core - asp.net

I have a Single Page Application written in JavaScript, and I use HTML5 history API to handle URLs on the client side. This means any URLs sent to the server should cause the server to render the same page.
In the ASP.NET MVC 5 I wrote this code to do this:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
// ...
routes.Add(new Route("{*path}", new MyRouteHandler()));
}
}
public class MyRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return WebPageHttpHandler.CreateFromVirtualPath("~/index.cshtml");
}
}
This worked really well. No matter what URL the server gets, it renders index.cshtml. Note that I am able to use a .cshtml file (as opposed to an .html file) which means I can have some C# code to dynamically change what .js scripts are included, append version numbers to .css files, and so on. What's more, I didn't have to implement controllers and views and so on just to render this .cshtml file.
Now we come to the question: How do you do this in ASP.NET Core? I have been reading the documentation, but I don't see how to render a .cshtml file without adding controller classes, view folders and other rigmarole.
Anyone know the equivalent code in ASP.NET Core?

Currently to run a CSHTML page "the normal way" in ASP.NET Core requires using ASP.NET Core MVC.
However, there is a planned feature that is being worked on that is somewhat similar to ASP.NET (non-Core) Web Pages, where you can have standalone CSHTML files. That feature is being tracked here: https://github.com/aspnet/Mvc/issues/494 (and as far as naming for the new feature, that is being tracked here: https://github.com/aspnet/Mvc/issues/5208).
There's also a sample of how to render an MVC view to a string (e.g. to generate an email, report, etc.), and that sample is available here: https://github.com/aspnet/Entropy/tree/dev/samples/Mvc.RenderViewToString
But to use this sample in the scenario you describe, you'd have to do some extra plumbing to wire it up as its own middleware (not a lot of plumbing; just a little!).
It's also worth noting that in your scenario you probably don't want all URLs going to this one view, because you still need the static files middleware running first to handle the CSS, JS, images, and other static content. Presumably you just want all other URLs to go to this dynamic view.

Related

Is it possible to use MVC without Asp.net pages?

I know that MVC is a design pattern that separates Model, View, Controller.
Model - Logic
View - Client View
Controller - connection between the two.
In case I want to change one of this things it will be easy just to change view\Model and the controller.
So is it possible to use only WebApi and MVC without Aps.Net pages (cshtml files)?
You can return html files
return new FilePathResult("path/FileName.html", "text/html");
And .cshtml files are Razor View Engine files, not Asp.Net pages.
You can alson change the view engine, see here for a list of .net view egines.
In short: yes, you can.
To elaborate: not sure what you mean, as .cshtml files are essentially the view part of MVC (the V part). ASP.NET MVC controllers by default return content of the .cshtml file by calling View() helper method.
But you can for example render html for the client inside your custom controller class without calling for a static html content. Or you might create WEB API project, with routing, models, and controllers, but no views - just plain data returned to the client.

ASP.NET Web.Api plugin architecture

Can you suggest me some articles or code samples about plugin architecture in web api?
Currently I'm thinking about this scenario: to have 1, centralized api gateway, where every client sends request, and have different applications controllers in Plugins folder. If someone wants to add new service, writes it's own controllers and puts dll files in Plugin folder.
For locating controller classes at run time, you can write an assembly resolver, like this.
public class MyAssembliesResolver : DefaultAssembliesResolver
{
public override ICollection<Assembly> GetAssemblies()
{
List<Assembly> assemblies = new List<Assembly>(base.GetAssemblies());
// Add all plugin assemblies containing the controller classes
assemblies.Add(Assembly.LoadFrom(#"C:\Plugins\MyAssembly.dll"));
return assemblies;
}
}
Then, add this line to the Register method in WebApiConfig.
config.Services.Replace(typeof(IAssembliesResolver), new MyAssembliesResolver());
With this, the request will still need to be sent to the individual controller even though the controller classes can come from assemblies in the plugin folder. For example, if MyAssembly.dll in the plugins folder contains CarsController, the URI to hit this controller will be /api/cars.

Error on run with simple ActionFilterAttribute

Started writing a simple filter to pull some stuff from request on each action load, copied some code from other stackoverflows that looks like so:
public class TestKeyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext context)
{
if (context.Request.Properties.ContainsKey("test"))
{
// do stuff
}
}
}
Then added the attribute with the rest:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
GlobalConfiguration.Configuration.Filters.Add(new ElmahHandledErrorLoggerFilter());
filters.Add(new HandleErrorAttribute());
filters.Add(new TestKeyAttribute());
}
On run, results in this error:
The given filter instance must implement one or more of the following filter
interfaces: IAuthorizationFilter, IActionFilter, IResultFilter, IExceptionFilter.
Most of the links I've found relate to MVC 3, and this seems to work; I am however using MVC 4 and using Web API - is there some other way I need to register the attribute now?
Just a note: I don't want the filter attached to Web API controllers (adding it to GlobalConfiguration.Configuration.Filters does work, though), but rather the normal web controllers.
Edit: I know I can get this working by inheriting from IActionFilter instead and using OnActionExecuting, I'm just curious why this approach doesn't work, since a bunch of tutorials seem to say it should.
I had the same error and was puzzled as ElmahHandledErrorLoggerFilter does implement IExceptionFilter.
After investigation, I kicked myself, I'd added the filters.Add(new ElmahHandledErrorLoggerFilter()); to the MVC site config under the FilterConfig class. Adding config.Filters.Add(new ElmahHandleErrorApiAttribute()); instead to the WebApiConfig class works.
Note: I'm using WebAPi v1 here but I've configured a v2 project in the same way.
The reason this doesn't work is that your filter is a WebAPI filter, which is not interchangeable with Mvc filters. The Mvc and WebAPI FilterAttribute and related classes and interfaces have many of the same names (which is why the tutorials appear to say this should work), but they live in different namespaces. Mvc filter classes live in System.Web.Mvc and WebAPI classes live in System.Web.Http.
Further reading here: https://stackoverflow.com/a/23094418/22392
When using MVC4, the project where your custom attribute resides must contain a reference to the System.Web.Http.Common library. I added it to my project using NuGet and hey presto! the error goes away.

How to localize urls with routing in ASP.NET WebForms application - not MVC

I have a webforms application (VB.net, .NET 4.0) which is translated in three languages and I managed to localize everything except the URLs. My preference is to use routing, so I'd like to find a solution in this direction (not URL rewriting or IIS rewrites, etc.)
Out of many read articles I stumbled into this one as well: http://blog.maartenballiauw.be/post/2010/01/26/Translating-routes-%28ASPNET-MVC-and-Webforms%29.aspx, but this example is for MVC application. (As are the most of the questions here as well)
Any comments and ideas are very much welcome!
I could implement Maarten's solution in a WebForms ASP.NET application as described below.
I downloaded his sample and opened with Visual Studio. As you see it the essential part of translated routing can be found in Routing folder.
I created a new Class Library project in my web application, and copied these 5 files. This project missed some assembly references, so I added System.Web and System.Web.Routing to it.
After this there was only one problem in TranslatedRouteCollectionExtensions class. The two extension methods used MvcRouteHandler. This is the only piece of code which depends on MVC. To eliminate this dependency modify both extension methods like this:
public static TranslatedRoute MapTranslatedRoute(
this RouteCollection routes,
string name,
string url,
object defaults,
object routeValueTranslationProviders,
IRouteHandler routeHandler,
bool setDetectedCulture)
{
TranslatedRoute route = new TranslatedRoute(
url,
new RouteValueDictionary(defaults),
new RouteValueDictionary(routeValueTranslationProviders),
setDetectedCulture,
routeHandler);
routes.Add(name, route);
return route;
}
I added a reference in my Web Application to this Class Library project.
With this modification Maarten's example of register a translated route changes as follows:
routes.MapTranslatedRoute(
"TranslatedRoute",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" },
new { controller = translationProvider, action = translationProvider },
new GeneralRouteHandler(),
true
);
Implementing a custom route handler is not a complicated process. You can find many good articles about it.

Configure IIS7 to server static content through ASP.NET Runtime

I searched high an low and still cannot find a definite answer.
How do I configure IIS 7.0 or a Web Application in IIS so that ASP.NET Runtime will handle all requests -- including ones to static files like *.js, *.gif, etc?
What I'm trying to do is as follows.
We have kind of SaaSy site, which we can "brand" for every customer. "Branding" means developing a custom master page and using a bunch of *.css and other images.
Quite naturally, I'm using VirtualPathProvider, which operates like this:
public override System.Web.Hosting.VirtualFile GetFile(string virtualPath)
{
if(PhysicalFileExists(virtualPath))
{
var virtualFile = base.GetFile(virtualPath);
return virtualFile;
}
if(VirtualFileExists(virtualPath))
{
var brandedVirtualPath = GetBrandedVirtualPath(virtualPath);
var absolutePath = HttpContext.Current.Server.MapPath(brandedVirtualPath);
Trace.WriteLine(string.Format("Serving '{0}' from '{1}'",
brandedVirtualPath, absolutePath), "BrandingAwareVirtualPathProvider");
var virtualFile = new VirtualFile(brandedVirtualPath, absolutePath);
return virtualFile;
}
return null;
}
The basic idea is as follows: we have a branding folder inside our webapp, which in turn contains folders for each "brand", with "brand" being equal to host name. That is, requests to http://foo.example.com/ should use static files from branding/foo_example_com, whereas http://bar.example.com/ should use content from branding/bar_example_com.
Now what I want IIS to do is to forward all requests to static files to StaticFileHandler, which would then use this whole "infrastructure" and serve correct files. However, try as I might, I cannot configure IIS to do this.
II7 already does that if the application pool's Managed Pipeline Mode is set to Integrated which is the default. In Integrated mode, ASP.NET handles all requests including those for static objects.
If you have to leave your application pool in Classic Mode then you need to use the same techniques you would use in IIS 6 to explicitly create handlers for the various static extensions.
Additional Information Based on Comments: I think your missing piece is creating an HttpHandler to handle the other extensions (.js, .css, etc.). Without this, then ASP.NET will use the default handling for these types of files. You would create a reference to you handler in your web.config. This article is an example of creating an HttpHandler for static files.
Kudos to everyone, but the problem was in totally different space.
VirtualPathProvider cannot be used in a pre-compiled web site. I'm furious.

Resources