views:

398

answers:

2

Hi guys

I have the following case where I want to accept the following routs

 '/type/view/23' or '/type/view/hats'

where 23 is the Id for hats.

The controller looks something like this:

public class TypeController
{ 
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult View(int id)
    {
      ...
    }
}

Now if they pass in 23 no problems. If they pass in hats, I have some work to do. Now I was wondering in this case would I translate hats to 23 by using an ActionFilter that looks to see if the value passed in as the id is an int (if so check that it exists in the database) or if it is a string looks up the database for what the id of the string that has been passed in is. In either case if a match is not found I would want redirect the user to a different action.

Firstly is the approach I have named correct, secondly is it posible to do a redirect from within an ActionFilter.

Cheers Anthony

A: 

Unsure you can do this. I would think that you'd need to pass in a string and then check to see whether it's a numeric but there may be a better way.

As for redirecting use

return RedirectToAction("MyProfile", "Profile");

You can pass route values as part of the RedirectToAction call so you can pass in id's or names etc if that is what's required.

There are other ones like redirecting to routes which may also be helpful for what you want.

griegs
With the redirection I was thinking of doing this from within the actionfilter
vdh_ant
+2  A: 

Change your signature to accept a string. Then check if the value of id is an int. If it is, then lookup by id, if not lookup by name. If you don't find a match, then do your redirect.

public class TypeController
{ 
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult View(string id)
    {
       Product product = null;
       int productID = -1;
       if (int.TryParse( id, out productID))
       {
           product = db.Products
                       .Where( p => p.ID == productID )
                       .SingleOrDefault();
       }
       else
       {
           product = db.Products
                       .Where( p => p.Name == id )
                       .SingleOrDefault();
       }

       if (product == null)
       {
           return RedirectToAction( "Error" );
       }
       ...
    }
}

The reason that I would do this is that in order to know what controller/actions to apply, the framework is going to look for one that matches the signature of the route data that's passed in. If you don't have a signature that matches -- in this case one that takes a string -- you'll get an exception before any of your filters are invoked. Unfortunately, I don't think you can have one that takes a string and another that takes an int -- in that case the framework won't be able to tell which one should match if a single parameter is passed, at least if it's a number, that is. By making it a string parameter and handling the translation yourself, you allow the framework to do its work and you get the behavior you want -- no filter needed.

tvanfosson
Thinking about finding a way for the framework to do the work, could I have two routs like the following:this._Routes.MapRoute("TypeInt", "type/view/{id}", new { controller = "Type", action = "ViewInt" }, new { page = @"^\d+$" } );this._Routes.MapRoute("TypeString", "type/view/{id}", new { controller = "Type", action = "ViewString" } );Would it be worth while doing this?? or do you think just do the try parse?
vdh_ant
I don't see what you gain by adding complication to the routing infrastructure, except that it does the parsing for you. I'm not smart enough to know if it would work without actually testing it. Personally, I'd probably have two different actions View and ViewByName, both of which use the same private method to generate the data once the correct product has been found. To me that would be even simpler and clearer.
tvanfosson