1

I have an action method that I use to retrieve a user's picture, however some of my users might have used Facebook to log in and I don't want to return the stored picture but the one from Facebook, so I was trying something like this:

public ActionResult Picture(long id)
{
  var user = UsersService.GetUser(id);

  if (user.IsConnectedWithFacebook)
  {
    var url = "http://graph.facebook.com/" + user.GetFacebookId() + "/picture?type=square";
    return Redirect(url);
  }

  return File(user.PictureData, user.PictureContentType);
}

and on the web page I just use:

<img src='@Url.Action("Picture", new {User.Id})' />

This works perfectly the first time I load the page, however the second time the image is not displayed. I checked calling just the action and the second time it shows a page with

"Object moved to here."

Which is why the img tag isn't loading any images.

Is there a way to prevent this from happening? I read in some other threads that people recommend to return a 301 (instead of the 302 returned by Redirect) but it returns the same page with the link (and the actual request returns a 200 instead of the 301 or 302).

Any idea on how to force it to return the redirect always?

I really don't want to add the logic to select which image to load to the page and I also don't want to request the data on the server and then return it to the page as it might be slow and become a bandwith problem.

Thanks for your help.

willvv
  • 8,439
  • 16
  • 66
  • 101
  • With respect to bandwidth and performance, the redirect option requires two round trips to the server to serve an image. Here is a related question: http://stackoverflow.com/questions/3778347/is-it-ok-to-http-redirect-images – Joe Enzminger Apr 18 '12 at 03:56
  • I'm aware of that, but I'm pretty confident that it will be much better to have the second request made to the FB servers. In the other case I would have to create a web request in the controller to the FB server, get the data and then return it to the client. Which means the second request will happen anyways, but in this case my server will be wasting the bandwith, not the client). – willvv Apr 18 '12 at 16:39
  • I can't see any reason why you would ever need to transfer the actual image from FB to your web server. I'd agree, that would be a very bad design. Definitely not what I'm proposing. – Joe Enzminger Apr 18 '12 at 17:05
  • Did you ever find a solution for this problem. I ran into it myself. I also don't want to use view logic as I will be handling multiple clients. – nVentimiglia Oct 28 '13 at 05:17

2 Answers2

0
if (user.IsConnectedWithFacebook)
{
  var url = "http://graph.facebook.com/" + user.GetFacebookId() + "/picture?type=square";
  return Redirect(url);
}

I think this code should return new ContentResult() { Content = url }; instead.

Also on the UI layer, I would recommend you to supply the Id as specified in parameter of the action.

<img src='@Url.Action("Picture")' />
Jonathan Liono
  • 556
  • 4
  • 13
  • Yep, the markup was a mistake creating the question, but it is correct in my code. What you suggest doesn't solve my problem, I'm not returning the url from the action method, I'm returning the actual image. What you suggest returns the url. – willvv Apr 18 '12 at 00:51
0

Rather than use redirection in your image controller, why don't you point the img src at the facebook url in your template....not sure the syntax is right (in fact, I'm sure it's not), but something like:

@if(user.IsConnectedWithFacebook)
{
    <img src="http://graph.facebook.com/"......
}
else
{
    <img='@Url.Action("Picture")' />
}

Alternatively, to be more elegant and reusable, move the logic into a static method on your controller:

<img src="@ProfileController.GetUserImageUrl(id) />

where

public static string GetUserImageUrl(long id)
{
    var user = UsersService.GetUser(id);

    if (user.IsConnectedWithFacebook)
    {
       return "http://graph.facebook.com/" + user.GetFacebookId() + "/picture?type=square";
    }
    return Url.Action("Picture");
}
Joe Enzminger
  • 11,110
  • 3
  • 50
  • 75
  • I just didn't want to add that logic to the View and redirecting seemed much simpler and "elegant" since the Profile action method will actually lead to the Profile picture being returned in all cases. – willvv Apr 18 '12 at 02:16
  • See updated answer. Should cover your elegance concerns and is more performant (not a word, but should be) than the redirection solution. – Joe Enzminger Apr 18 '12 at 03:57
  • Interesting, although it might be a very bad idea to have static methods in controllers, a lot of unexpected results might occur unless you use locking, which will cause a bottleneck in the method. I still want to use the redirect, I just want to know who's responsible for returning the page with the link to the new location (ASP.NET? IIS? the browser?) – willvv Apr 18 '12 at 16:36
  • Your fears about static methods are unfounded. You don't have to put the GetUserImageUrl method in the controller. It can be anywhere in the application if you don't want it in the controller. As for bottlenecks, with the exception of potentially your UsersService (which needs to be threadsafe anyway if you are using it in a web application), there is nothing about this method that would require locking. If you're dead set on using redirection, I think you'll find that solution way more complicated than what I've proposed here. – Joe Enzminger Apr 18 '12 at 17:03