0

In MVC6, is there a way to specify the actions in my URL to be querystrings instead of a path?

E.G. http://localhost/Index2 and http://localhost/Edit

Both of these fire the Index2 and Edit actions in my controller respectively. But I'm working with a web site where we're only allows to have one single URL (long story)... so it it possible to fire the exact same actions by navigating to respective urls like

http://localhost/Default.aspx?action=Index2 and http://localhost/Default.aspx?action=Edit

I suppose what I could do is just take the dozen or so action functions in my controller and combine them ALL into the Index action and based on the "action" querystring parameter, do a switch/case-select statement and copy each of the original action/subs into their respective chunks under each CASE. But I was hoping there would be something could be done that seemed a little cleaner.

FYI: I don't use querystrings for anything else. All my values are passed from action to action by POSTS

citronsmurf
  • 90
  • 11

2 Answers2

0

Yes, you use the Route decorator but instead of adding a route string, you add an empty string. Example:

namespace TestAPI.Controllers
{
    [RoutePrefix("api/testapi")] // eg: http://localhost/api/testapi?param1=foo&param2=bar
    public class TestAPIController : BaseAPIController
    {
        [HttpGet]
        [Route("")] // so that ASP MVC recognizes query strings
        public HttpResponseMessage Get()
        {

            // get the query parameters into a collection
            var queryparams = Request.GetQueryNameValuePairs().Select(q => new { q.Key, q.Value });


            // declare main keys value pairs
            string param1 = queryparams.Where(k => k.Key == "param1").Select(v => v.Value).FirstOrDefault();
            string param2 = queryparams.Where(k => k.Key == "param2").Select(v => v.Value).FirstOrDefault();

            // Do your stuff

            // Return a response


        }
    }
}
Iskandar Reza
  • 953
  • 1
  • 7
  • 16
  • Since my actions currently are accepting parameters via post and you have the [HttpGet] attribute up there, would this still work? In order to continue recceiving the posted fields I'd have to change your example to [HttpPost] (while still collecting the querystring to get the action name) – citronsmurf Jan 11 '18 at 19:22
  • Oops sorry, I didn't see that. Yes changing it to `[HttpPost]` should work. I thing you also need to change the method invocation to something like `public HttpResponseMessage Post([FromUri]param1 value)` – Iskandar Reza Jan 11 '18 at 19:28
0

This sort of thing is pretty easy to do by subclassing RouteBase. It allows you to use virtually any logic you want to analyze the request and direct it to specific action methods depending on whats in the request.

Here is a route using your example of http://localhost/Default.aspx?action=Index2 and http://localhost/Default.aspx?action=Edit

public class CustomRoute : RouteBase
{
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        RouteData result = null;

        // Trim off the leading "/"
        var path = httpContext.Request.Path.Substring(1);

        if (path.Equals("Default.aspx", StringComparison.OrdinalIgnoreCase))
        {
            result = new RouteData(this, new MvcRouteHandler());

            result.Values["controller"] = "Home";
            result.Values["action"] = httpContext.Request.QueryString["action"] ?? "Index";
        }

        // IMPORTANT: Returning null tells the routing framework to try
        // the next registered route.
        return result;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        // Returning null skips this route.
        // Alternatively, implement a scheme to turn the values passed here
        // into a URL. This will be used by UrlHelper to build URLs in views.
        return null;
    }
}

It can be registered with MVC like this.

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.Add(new CustomRoute());

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
            namespaces: new string[] { "WebApplication23.Controllers" }
        );
    }
}

Do note that the order routes are registered is important.

There is one more thing if you insist on using a filename.extension pattern - you have to configure IIS to allow the request to reach MVC because by default any URL with a . in it will throw a 404 not found error.

<system.webServer>
  <handlers>
    <add name="ApiURIs-ISAPI-Integrated-4.0"
      path="/Default.aspx"
      verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
      type="System.Web.Handlers.TransferRequestHandler"
      preCondition="integratedMode,runtimeVersionv4.0" />
  </handlers>
</system.webServer>
NightOwl888
  • 55,572
  • 24
  • 139
  • 212