Custom route authorization

Topics: Customizing Orchard
Jul 31, 2013 at 10:19 PM
Edited Jul 31, 2013 at 10:32 PM
I have a custom route that serves up JSON to a mobile app, works fine when I am logged into the site, but not when logged out. My action method runs, but something after my it clears the content.

How do I allow anonymous access to a controller in a module?

EDIT: I have tried [AlwaysAccessible], but same result.

My route is declared as
new RouteDescriptor {
                    Priority = 100,
                    Route = new Route(
                        "mobile/Ad/Index",
                        new RouteValueDictionary {
                            {"area", "MyModule"},
                            {"controller", "Mobile"},
                            {"action", "Ad"}
                        },
                        new RouteValueDictionary(),
                        new RouteValueDictionary {
                            {"area", "MyModule"}
                        },
                        new MvcRouteHandler())
                }
Coordinator
Jul 31, 2013 at 10:46 PM
It should just work. What's behind that route?
Jul 31, 2013 at 11:02 PM
Even returning some dummy data does not work. It does work when I am authenticated.

Brian
Jul 31, 2013 at 11:10 PM
Here is the controller and the route (I have an isolated module to test this)

codegen module M2

you will need to add a JSON.NET via nuget.
Routes:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Mvc.Routes;

namespace M2 {
    public class Routes : IRouteProvider {
        public IEnumerable<RouteDescriptor> GetRoutes()
        {
            return new[]
                {
                    new RouteDescriptor
                        {
                            Priority = 100,
                            Route = new Route(
                                "M2/Wish/Index",
                                new RouteValueDictionary
                                    {
                                        {"area", "M2"},
                                        {"controller", "Wish"},
                                        {"action", "Index"}
                                    },
                                new RouteValueDictionary(),
                                new RouteValueDictionary
                                    {
                                        {"area", "M2"}
                                    },
                                new MvcRouteHandler())
                        }
                };
        }

        public void GetRoutes(ICollection<RouteDescriptor> routes)
        {
            foreach (var routeDescriptor in GetRoutes())
                routes.Add(routeDescriptor);
        }
    }
}
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json;

namespace M2.Controllers {
    public class WishController : Controller {
        public ActionResult Index()
        {
            return JsonNet(new {Wish = "This would work"});
        }

        protected virtual JsonNetResult JsonNet(object data) {
            var result = new JsonNetResult() { Data = data };
            return result;
        }
    }

    public class JsonNetResult : ActionResult {
        public Encoding ContentEncoding { get; set; }
        public string ContentType { get; set; }
        public object Data { get; set; }

        public JsonSerializerSettings SerializerSettings { get; set; }
        public Formatting Formatting { get; set; }

        public JsonNetResult() {
            SerializerSettings = new JsonSerializerSettings();
        }

        public override void ExecuteResult(ControllerContext context) {
            if (context == null)
                throw new ArgumentNullException("context");

            HttpResponseBase response = context.HttpContext.Response;

            response.ContentType = !string.IsNullOrEmpty(ContentType)
                                       ? ContentType
                                       : "application/json";

            if (ContentEncoding != null)
                response.ContentEncoding = ContentEncoding;

            if (Data != null) {
                JsonTextWriter writer = new JsonTextWriter(response.Output) { Formatting = Formatting };

                JsonSerializer serializer = JsonSerializer.Create(SerializerSettings);
                serializer.Serialize(writer, Data);

                writer.Flush();
            }
        }
    }
}
Browse to site/M2/Wish/Index logged in, then logout and try again.

Using 1.7

Brian
Coordinator
Aug 1, 2013 at 12:11 AM
Well, as an aside, there are much simpler ways to return JSON from a controller action. Did you try the same thing but returning a simple view? Just to isolate variables.
Aug 1, 2013 at 1:13 AM
Edited Aug 1, 2013 at 1:24 AM
I have not tried a view, not even sure how to accomplish that. This is my second day working with Orchard, so any pointers to the much simpler ways to return JSON from a controller action (besides the built in Json in MVC, I am open).

I did try returning Content, same result, works when logged in, does not work when not logged in.
public ActionResult Index()
        {
            //return JsonNet(new {Wish = "This would work"});
            return Content("Wish this would work", "text/plain");
        }
Aug 1, 2013 at 1:36 AM
Just tried this with 1.6.1 and it works. Anyway to file a bug report?
Coordinator
Aug 1, 2013 at 2:07 AM
What's wrong with the built-in Json in MVC?

To file bugs: https://orchard.codeplex.com/WorkItem/Create
Coordinator
Aug 1, 2013 at 3:57 AM
I'm really doubting this. I've verified several controller actions as an anonymous user on a 1.7 build, and I'm not seeing any problem. Something else must be the matter here.
Coordinator
Aug 1, 2013 at 4:00 AM
What I'd do at this point is try to modify things one at a time, remove or alter variables one by one (action name, controller name, route priority, etc.)
Aug 1, 2013 at 3:06 PM
I have narrowed it down so that the only code is this as the controller (no routes, no nothing). Started with fresh download (not source), setup IIS (on windows 8), run through the setup. Would you be able to try the steps below and verify?

Steps
Orchard command line
feature enable Orchard.CodeGeneration
codegen module M2

Add the WishController to Controllers directory (code below)
using System.Web.Mvc;

namespace M2.Controllers {
    public class WishController : Controller {
        public ActionResult Index()
        {
            return Content("Wish this would work without being logged in", "text/plain");
        }
    }
}
Add this to the csproj
<Compile Include="Controllers\WishController.cs" />
Back to orchard command line
feature enable M2

browse to /M2/Wish/Index - result is text
Back to home page, click "sign out"
browse to /M2/Wish/Index (might need to ctrl-refresh) - result is a blank page, no auth error, nothing.

NOTES:
If you view through fiddler you will see that when not logged in, the content length is 0.
If you debug this, the controller action fires and creates the content, something after is clearing it.
The same steps work with 1.6.1




As for JSON,
  1. Performance - check the chart at bottom of https://json.codeplex.com/
  2. Dates with the JavaScriptSerializer (total pain with mobile libraries that just do not get it) - http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx
  3. Web API now uses JSON.net - basic consistency - http://encosia.com/jquery-asp-net-web-api-and-json-net-walk-into-a-bar/
Coordinator
Aug 1, 2013 at 8:07 PM
Thanks for the repro and the bug report. This is really weird.
Developer
Aug 5, 2013 at 9:47 PM
Do you, by any chance, have output caching enabled?
Aug 5, 2013 at 10:23 PM
If it is on out of the box I do, if not, then no.
Coordinator
Aug 5, 2013 at 11:50 PM
Can you check?
Aug 6, 2013 at 1:26 AM
Sure. What am I looking for?
Coordinator
Aug 6, 2013 at 2:12 AM
Is the output caching feature enabled?
Aug 6, 2013 at 1:36 PM
The output cache module was enabled. When I disabled it, I started getting the correct response when not logged in.