MVC: Serve file with http header - asp.net

I am trying to follow this guide and store files in a sub-folder.
However, the OneSignal guide asks to serve these files with additional HTTP header Service-Worker-Allowed: /. How do I do that in Asp.Net MVC?

You can use in your controller's action :
this.Response.Headers.Add("Service-Worker-Allowed","/");
Hope this help :-)
Edit :
A better way to do this is to create an action filter to automatically add this header :
public class WorkerAllowedAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Response.Headers.Add("Service-Worker-Allowed", "/");
base.OnActionExecuted(filterContext);
}
}
And you can put it on your action (or your controller) :
public partial class HomeController
{
[WorkerAllowed]
public ActionResult Index()
{
return View();
}
}

No matter what I tried, I could not get the scope allowed even though I could see it in the response header. I figured out a solution based on this article: https://weblog.west-wind.com/posts/2015/Nov/13/Serving-URLs-with-File-Extensions-in-an-ASPNET-MVC-Application
Basically, we are making the service worker js file appear that it 'lives' in the root directory to avoid scope issues. You can leave your service worker js file in whatever directory you want (probably /Scripts). Then you set up an IIS rewrite rule in web.config (or in IIS) that re-routes any request that includes the service worker file name to an action (be sure you specify the exact path to the action). The action then returns the file as type javascript/application.
<system.webServer>
<rewrite>
<rules>
<rule name="Service worker fix">
<match url="pwa-sw.js"/>
<action type="Rewrite" url="/home/serviceworker"/>
</rule>
</rules>
</rewrite>
</system.webServer>
Then the controller
[AllowAnonymous]
[Route("serviceworker")]
public ActionResult serviceworker()
{
return File("/Scripts/pwa-sw.js", "application/javascript");
}

Related

SignalR /negotiate is making requests to /Account/Login - I have no Account/Login endpoint

I'm seeing lots of entries in my logs from this request:
/signalr/negotiate
The error is:
The controller for path '/Account/Login' was not found or does not implement IController
I have a JS client connecting to an AppHub that requires authentication:
[Authorize]
[HubName("appHub")]
public class AppHub : Hub
{
// content
}
This is happening because there's an 'signalr` session alive with an expired cookie attempting to connect:
I'm not sure why the request is automatically seeking out this page. It's not specified anywhere in my web.config, routes, or elsewhere. Why is this happening?
I'd like to prevent the signalR client from attempting to connect if the user is unauthenticated. How can this be achieved?
If I understand your issue correctly then you are going to want to create your own custom class to handle this by inheriting the AuthorizeAttribute class: https://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute(v=vs.118).aspx
For example:
public class MyCustAuthorize : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new HttpUnauthorizedResult();
}
else
{
//modify this to do whatever you want to happen if unauthorized
filterContext.Result = new RedirectResult("/session/noaccess/");
}
}
}
Then you can decorate it with your custom class instead of the default authorize
(example is from a mvc controller but should be able to function the same on your hub)
So this:
[Authorize]
public class AdminController : Controller
{
Becomes this:
[MyCustAuthorize]
public class AdminController : Controller
{
I believe the /Account/Login is the default path for forms auth so that is why it is directing there if it is not defined within your config file.
Alternatively you could insert the specific url to redirect to if that is what you are searching for by placing the following loginUrl attribute value in your auth section > forms element in the web.config:
It looks like this may be similar to these other answers to questions already asked here and these may provide your solution:
Stackoverflow 1
Stackoverflow 2
Have you tried stopping the connection in the client when they are no longer authorized?
$.connection.hub.stop();
Your app is using FormsAuthentication, so the Authorize attribute is redirecting to the login page by default when it fails to authorize.
You can disable this by adding the following to your web.config:
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication" />
</modules>
This will remove all the default behaviors.
You might have something in your app.config that looks like
<membership defaultProvider="ClientAuthenticationMembershipProvider">
<providers>
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
</providers>
</membership>
Which is what is adding these default behaviors for you.

ASP.NET MVC5 Customised Inbound Routing

I'm "playing" around with custom inbound URL routing and have came across a problem.
When I pass my custom route a URL to examine, that ends in *.+, my class is not fired when i submit the request.
An example URL would be "~/old/windows.html"
When I step through this in the debugger, my RouteBase implementation doesn't fire. If i edit the url that i pass to the constructor of my route to try to match against "~/old/windows", my implemetation is fired as expected.
Again, If i change the url ro examine to "~/old/windows." the problem reoccurs.
My Route Implementation is below :-
public class LegacyRoute : RouteBase
{
private string[] _urls;
public LegacyRoute(string[] targetUrls)
{
_urls = targetUrls;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (_urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))
{
result = new RouteData(this, new MvcRouteHandler());
result.Values.Add("controller", "Legacy");
result.Values.Add("action","GetLegacyURL");
result.Values.Add("legacyURL", requestedURL);
}
return result;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
In the RoutesConfig file I have registered my route like so :-
routes.MapMvcAttributeRoutes();
routes.Add(new LegacyRoute(new[]{"~/articles/windows.html","~/old/.Net_1.0_Class_Library"}));
Can anyone point out why there is a problem?
By default, the .html extension is not handled by .NET, it is handled by IIS directly. You can override by adding the following section in Web.config under <system.webServer> -
<handlers>
<add name="HtmlFileHandler" path="*.html" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
As pointed out here. The above will route EVERY .html file request to .NET, you might want to be more specific by providing a more complete path if you don't want your routing to handle every .html file.
I've found the problem, and I'm sure this will help out a lot of fellow developers.
The problem is with IIS Express that is running via Visual Studio.
There is a module configured in the applicationhost.config called :-
UrlRoutingModule-4.0
This is how it looks in file :-
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler,runtimeVersionv4.0" />
You need to set the preCondition Parameter to "".
To do this :-
Run you app via Visual Studio
Right click on IIS Express in your system tray, select "Show All Applications"
Click on the project you wish to edit, then click the config URL.
Open the file with Visual Studio, Locate the module and ammend.
Hope this helps anyone else, who ran into a similar problem.

Dynamic Routing with Web API

I have a WebAPI controller with a Get method as follows:
public class MyController : ApiController
{
public ActionResult Get(string id)
{
//do some stuff
}
}
The challenge is that I am attempting to implement WebDAV using Web API. What this means is that as a user browses down a folder structure the URL will change to something like:
/api/MyController/ParentFolder1/ChildFolder1/item1.txt
Is there a way to route that action to MyController.Get and extract out the path so that I get:
ParentFolder1/ChildFolder1/item1.txt
Thanks!
"Dynamic" route is not a problem. Simply use wildcard:
config.Routes.MapHttpRoute(
name: "NavApi",
routeTemplate: "api/my/{*id}",
defaults: new { controller = "my" }
);
This route should be added before default one.
Problem is that you want to end URL with file extension. It will be interpreted as static request to .txt file.
In IIS7+ you can work around that by adding line in web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
Don't forget that if you use MyController, then route segment is just "my"
Use the NuGet package "AttributeRouting Web API". You can specify specific routes for each action, including dynamic parameters.
I was just dealing with this so try it out, and come back if you need more help.

Configure Spring MVC with AngularJS

I would like to be able to use Spring MVC as REST server and AngularJS on client side.
I have several urls for REST :
/rest/products
/rest/products/{id}
And i have several urls for the UI :
/shop/products
/shop/products/{id}
Since it is AngularJS which do the trick on client side, i just want to be able to redirect all default ui urls (not the rest ones) to the index.html file used by AngularJS.
So, in Spring MVC configuration, i would like to be able to do something like that :
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "com.mypackage.web")
public class WebAppConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/**").setViewName("index");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
With that, i want to delegate all the UI urls handling to AngularJS.
I also want that if the user writes a bad url in the browser he would be redirected by Spring MVC on the index.html file and it will be AngularJS which will do the redirect on the error ui page. I have seen several projects on the web with a single index.html file, but no one handle this error case.
I have been struggling a lot of time trying to do this trick but i can't find a solution.
So my question is : how can i do that ? and more generally, am i wrong with this Spring MVC-AngularJS wanted configuration ?
Very important : I use Spring MVC 3.2 and Tomcat 7.34 without web.xml (full Servlet 3.0)
Thanks a lot.
Maybe it is possible to solve it via $routeProvider:
$routeProvider.when('/redirect/:redirectParams', {templateUrl: 'partials/homePartial', controller: redirectController});
$routeProvider.when('/home', {templateUrl: 'partials/homePartial', controller: homeController});
$routeProvider.when('/someRoute', {templateUrl: 'partials/somePartial', controller: someController});
$routeProvider.when('/error/', {templateUrl: 'partials/errorPartial', controller: errorController});
$routeProvider.when('/error/:errorMessage', {templateUrl: 'partials/errorPartial', controller: errorController});
$routeProvider.otherwise({redirectTo: '/error'})
Just let the $routeProvider redirect to an error page when the route was not found.
Edit:
I've added a redirectController in the above example. This controller will read the $routeParams, in this case $routeParams.redirectParams and use $location to change the route to /error.
Spring just redirects to http://host:port/index.html/#/redirect/error=blabla. You could and should probably finetune this and support multiple routeParams.
In Spring you would have basically three request mappings:
REST request mapping
index.html request mapping
other urls request mapping
To redirect all other requests:
#RequestMapping(value = "{path}", method = RequestMethod.GET)
public String redirect(#PathVariable String path) {
String route = null;
if (path.equals("/") || path.startsWith("/index.html")) {
// load index.html
} else {
route = "redirect:/index.html/#/redirect/" + path;
}
return route;
}
I think you can write an Interceptor which allows some known URLs like /rest/, /resources/, /webjars/* and for any other URLs redirect to index.html.
Try using urlrewritefilter
This should do the work for you. You'll have to configure/add it in your web.xml to every url would be handled and you can manipulate it to redirect to the index.html
I know its a bit late but in case someone run into the same trouble here's the URL filter sample put in your classpath urlwrite.xml
You can set redirection of certain urls you want to filter.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN"
"http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd">
<!-- Configuration file for UrlRewriteFilter http://www.tuckey.org/urlrewrite/ -->
<urlrewrite>
<rule>
<note>
The rule means that requests to /test/status/ will be redirected
to
/rewrite-status
the url will be rewritten.
</note>
<from>/index.ipxs/directory</from>
<to type="redirect">/index.ipxs/marketplace</to>
</rule>
<rule>
<from>/index.ipxs/faq</from>
<to type="redirect">/index.ipxs/marketplace</to>
</rule>
<rule>
<from>/index.ipxs/marketplace</from>
<to type="redirect">/iPlexus/index.ipxs</to>
</rule>
</urlrewrite>

Using ASP.NET routing to serve static files

Can ASP.Net routing (not MVC) be used to serve static files?
Say I want to route
http://domain.tld/static/picture.jpg
to
http://domain.tld/a/b/c/picture.jpg
and I want to do it dynamically in the sense that the rewritten URL is computed on the fly. I cannot set up a static route once and for all.
Anyway, I can create a route like this:
routes.Add(
"StaticRoute", new Route("static/{file}", new FileRouteHandler())
);
In the FileRouteHandler.ProcessRequest method I can rewrite the path from /static/picture.jpg to /a/b/c/picture.jpg. I then want to create a handler for static files. ASP.NET uses the StaticFileHandler for this purpose. Unfortunately, this class is internal. I have tried to create the handler using reflection and it actually works:
Assembly assembly = Assembly.GetAssembly(typeof(IHttpHandler));
Type staticFileHandlerType = assembly.GetType("System.Web.StaticFileHandler");
ConstructorInfo constructorInfo = staticFileHandlerType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
return (IHttpHandler) constructorInfo.Invoke(null);
But using internal types doesn't seem to be the proper solution. Another option is to implement my own StaticFileHandler, but doing so properly (supporting HTTP stuff like ranges and etags) is non-trivial.
How should I approach routing of static files in ASP.NET?
Why not use IIS to do this? You could create a redirect rule to point any requests from the first route to the second one before it even gets to your application. Because of this, it would be a quicker method for redirecting requests.
Assuming you have IIS7+, you would do something like...
<rule name="Redirect Static Images" stopProcessing="true">
<match url="^static/?(.*)$" />
<action type="Redirect" url="/a/b/c/{R:1}" redirectType="Permanent" />
</rule>
Or, if you don't need to redirect, as suggested by #ni5ni6:
<rule name="Rewrite Static Images" stopProcessing="true">
<match url="^static/?(.*)$" />
<action type="Rewrite" url="/a/b/c/{R:1}" />
</rule>
Edit 2015-06-17 for #RyanDawkins:
And if you're wondering where the rewrite rule goes, here is a map of its location in the web.config file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<!-- rules go below -->
<rule name="Redirect Static Images" stopProcessing="true">
<match url="^static/?(.*)$" />
<action type="Redirect" url="/a/b/c/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
After digging through this problem for a few hours, I found that simply adding ignore rules will get your static files served.
In RegisterRoutes(RouteCollection routes), add the following ignore rules:
routes.IgnoreRoute("{file}.js");
routes.IgnoreRoute("{file}.html");
I've had a similar problem. I ended up using HttpContext.RewritePath:
public class MyApplication : HttpApplication
{
private readonly Regex r = new Regex("^/static/(.*)$", RegexOptions.IgnoreCase);
public override void Init()
{
BeginRequest += OnBeginRequest;
}
protected void OnBeginRequest(object sender, EventArgs e)
{
var match = r.Match(Request.Url.AbsolutePath);
if (match.Success)
{
var fileName = match.Groups[1].Value;
Context.RewritePath(string.Format("/a/b/c/{0}", fileName));
}
}
}
I came up with an alternative to using the internal StaticFileHandler. In the IRouteHandler I call HttpServerUtility.Transfer:
public class FileRouteHandler : IRouteHandler {
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
String fileName = (String) requestContext.RouteData.Values["file"];
// Contrived example of mapping.
String routedPath = String.Format("/a/b/c/{0}", fileName);
HttpContext.Current.Server.Transfer(routedPath);
return null; // Never reached.
}
}
This is a hack. The IRouteHandler is supposed to return an IHttpHandler and not abort and transfer the current request. However, it does actually achieve what I want.
Using the internal StaticFileHandler is also somewhat a hack since I need reflection to get access to it, but at least there is some documentation on StaticFileHandler on MSDN making it a slightly more "official" class. Unfortunately I don't think it is possible to reflect on internal classes in a partial trust environment.
I will stick to using StaticFileHandler as I don't think it will get removed from ASP.NET in the foreseeable future.
You need to add TransferRequestHandler for handling your static files.Please see following answer
https://stackoverflow.com/a/21724783/22858

Resources