How to load an image from ASP.NET MVC controller in a Facebook canvas app? - asp.net-mvc-3

In my Facebook canvas application, I want to load an image from my ASP.NET MVC Controller.
So I do somethinhg like this, in my controller (simplified):
public ActionResult GetImage(int id)
{
// ....
return File(#"c:\temp\1.jpg", "image/jpeg");
}
And it's called from my aspx page like so:
<img src="/Home/GetImage?id=20" alt="Test"/>
But the image is not displayed, I get a "red x".
So I fire up Fiddler and see that the request does indeed return the image, but the data is prepadded with Facebook's frame html:
<html><head><script type="text/javascript">
top.location = "http://www.facebook.com/dialog/oauth/?state=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&client_id=xxxxxxxxxxxxxxx&redirect_uri=http://localhost:5000/facebookredirect.axd";
</script></head><body></body></html>?????JFIF??H?H????
?ICC_PROFILE???
????????mntrRGB XYZ ?????$?acsp???????????????????????????????????
Which of course is not vaild jpeg image data.
How can I avoid this pre padded data?

Never mind, I found out myself that the signed_request data was (yet again) missing.
I thought the only way to pass this was thru a HTTP header, but (like magic) I could pass it as a parameter as well causing the request to be validated.
<img src="/Home/GetImage?id=20&signed_request=<%=Request.Params["signed_request"] %>" />
public ActionResult GetImage(int id), string signed_request)
{
var auth = new CanvasAuthorizer();
if (auth.Authorize())
{
// Indeed, I'm now authorized
return File(#"c:\temp\1.jpg", "image/jpeg");
}
/// ...
}
/M

Related

Best Practice for showing Page after Post

I have a View with a Form that calls a controller action Post Method to "Complete" a Package. It then needs to refresh the page its on as that contains information that will be updated, both within the view itself and also within a partial. It does use two different Controllers in different MVC Areas.
The Post works correctly and the redirect is issued, but the page is not refreshed.
I have read that instead, I should use OnSuccess within the Ajax call that calls Complete, but I thought that was for in page calls, not ones that navigate to different pages.
View Form
#using (Ajax.BeginForm("Complete", "Packages", new { Area = "Core" },
new AjaxOptions
{
HttpMethod = "POST"
}))
{
Core(Area) Packages Controller
[HttpPost]
public ActionResult Complete(int ID)
{
// Update code
// Refresh the full page
return RedirectToAction("Summary", new { Area = "Control", id = packageBuilder.CurrentPackage.ID });
}
Control (Area) Packages Controller
[HttpGet]
public ActionResult Summary(int id)
{
// Get Model
return View("Summary", model);
}
Any pointers would be warmly welcomed.
Thanks,
Chris.
The reason that your page is not refreshed after you submit the form and the redirect is not issued in the browser, is that you are submitting the request over AJAX. This is a request issued by the browser behind the scenes.
If you want to submit the form and for the page to be refreshed, I'd recommend changing your code from Ajax.BeginForm(... to Html.BeginForm(... and then it will load the page and perform the redirect as expected.
I am not quite sure how your ajax calls are structured, but if you are using the MVC Ajax helper you can just call `location.reload(); in the OnComplete method, like so:
#using (Ajax.BeginForm(new AjaxOptions{OnComplete = "javascriptfunction"}))
{
//Data and submit button
}
<script>
function javascriptfunction(){
location.reload();
}
</script>

FileContentResult render in View without specific Controller Action

I have same action in a controller that fetch from db for example a Employee with a name, age, image (byte[]).
So I store the info in ViewBag. Then in the View the specific code for the image is:
<img src="#ViewBag.Image" alt="Logo" />
But when I see the content of the response I have:
<img src="System.something.FileContentResult" alt="Logo" />
I have seen a lot of examples that gets the FileContentResult from a specific Action in a Controller for example:
(The code is an example only)
public ActionResult GetEmployeeImage (int id){
byte[] byteArray = GetImageFromDB(id);
FileContentData file = new File (byteArray,"image/jpg");
return file;
}
And then render the image with:
<img src="#Url.Action("GetEmployeeImage", "Home", new { id = ViewBag.Id })" alt="Em" />
But I dont want that because I already have the image.
How can I render it to the View throw the ViewBag?
Thanks!
PD: Rendering image in a View is the same but was not concluded
One possibility is to use the data URI scheme. Bear in mind though that this will work only on browsers that support it. The idea is that you would embed the image data as base64 string into the src attribute of the img tag:
<img src="data:image/jpg;base64,#(Html.Raw(Convert.ToBase64String((byte[])ViewBag.Image)))" alt="" />
If on the other hand you want a solution that works in all browsers you will have to use a controller action to serve the image. So in the initial request fetch only the id, name and age properties of the employee and not the image. Then write a controller action that will take the employee id as parameter, query the database, fetch the corresponding image and return it as file result. Then inside the view point the src attribute of the img tag to this controller action.
Based on the answer of #darin , if any one wants to make it via calling a controller's action:
public FileContentResult GetFileByID(string ID)
{
try
{
// Get the object from the db
Ent ent = Biz.GetPatientsByID(ID);
// Convert the path to byte array (imagepath is something like: \\mysharedfolder\myimage.jpg
byte[] doc = EFile.ConvertToByteArray(ent.ImagePath);
string mimeType = File(doc, MimeMapping.GetMimeMapping(Path.GetFileName( ent.ImagePath))).ContentType;
Response.AppendHeader("Content-Disposition", "inline; filename=" + Path.GetFileName(ent.ImagePath));
return File(doc, mimeType);
}
catch (Exception ex)
{
throw ex;
}
}
In the front end:
<img id="ItemPreview" src="#Url.Action("GetFileByID","Patient", new {ID= Model.ID })" alt="" width="350" height="350" />
Where Patient is the controller name.

Lost: Getting a fully rendered view via Ajax in ASP.NET MVC3 / jQuery

All,
I am very new to MVC3 / jQuery combo and have been reading tutorials. While I kind of get the concept, razor syntax etc. I'm still a bit confused on how to implement a basic concept that I'm trying to.
I have a textarea and when someone enters some text into it and hits enter, I want to trigger a ajax call to the server with the content of the text area, and get back a fully formed HTML blurb that I can put in a div. Now as I understand in MVC3 this would be a view, so in a sense I'm rendering a view on the server and sending it back so I can put it in HTML.
Is this possible? Any examples that I can look up to see how this done? I know how to capture keystrokes, get the value etc., it's this partial rendering of a fully formed HTML via ajax that I'm struggling to understand.
Thanks,
You can do with jQuery. This is how it works. you listen for the keydown event of the text area and when there is a keydown, check what key it is.if it is enter key, then make a jQuery ajax post call to a server page (action method in your controller with the data).Save the data there to your tables and return the markup of what you want and return that. in your script load it to your relevant div.
HTML
//Load jQuery library in your page
<textarea id="txtComment" > </textarea>
<div id="divComment"></div>
Javascript
$(function(){
$("#txtComment").keydown(function (event) {
if (event.keyCode == 10 || event.keyCode == 13) {
var comment=$("#txtComment").val();
comment=encodeURIComponent(comment);
$.post("yourcontroller/actionmethod?data="+comment,function(response){
$("#divComment").html(response);
});
}
});
});
and your controller action method
public ActionResult actionmethod(string data)
{
//Do some sanitization on the data before saving.
// Call your method to save the data to your tables.
CommentViewModel objCommentVM=new CommentViewModel();
objCommentVM.Comment=data;
return View("PartialCommentView",objCommentVM);
}
You should have a ViewMolde class called "CommentViewModel" like this
public class CommentViewModel
{
public string Comment{ set; get; }
}
and you should have a View called PartialCommentView which is strongly typed to CommentViewModel
#model FlashRack.ViewModel.RackViewModel
#{
Layout = null;
}
<div>
#Model.Comment
</div>
If you are simply returning a string, instead of returning a View, you can simply return the string using Return Content("your string here") method as well. But i prefer returning the ViewModel via View because it is more scalable and clean approach to me.
Your action method will return the markup you have in your PartialCommentView with the data.
Keep in mind that you have to take care of the special characters and escaping them properly.

Refreshing parent view when a partial view's form is submitted

I'm looking into using partial views in MVC3 using Razor, and I get my partial view to render and it works fine.
What I'd like to do, though, is refresh the parent view when the partial view is submitted.
Code in my parent view to render partial view
<div id="mydiv">
#{ Html.RenderAction("Add", "Request"); }
</div>
Action for parent view is simple,
public ActionResult Index()
{
List<obj> reqs = //some query
return View(reqs);
}
In my partial view's get action I have:
public ActionResult Add()
{
AddRequestViewModel vm = new AddRequestViewModel();
//set some stuff on the VM here
return PartialView(vm);
}
In the post action called by the partial view, if modelstate isn't valid, return PartialView(vm)
If it is valid, I'd like the parent and partial views to refresh.
I tried RedirectToAction, but this can't be called in an action called by a partial, apparently, and I tried return Index();, but this causes an issue with the code used to render the partial view,
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'System.Collections.Generic.List'1[DatRequests.Models.ReqRequest]', but this dictionary requires a model item of type 'DatRequests.ViewModels.AddRequestViewModel'.
Any suggestions on how to do this would be appreciated. The purpose of the page is to show a list of elements, and the partial contains a form to add a new element to the list.
Edit: The partial's model is different, as it contains data for selection, which is from a db, which is why I tried RenderAction, but I'm not sure if there are other ways of doing this.
When the partial view is submitted normally you submit it to some controller action. You could either submit it using a normal request or an AJAX request. If you use a normal request you could perform a standard redirect to the Index inside the POST controller action that will handle the form submission. If you use AJAX, you could return a JSON result pointing to the url that you want to redirect:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
if (!ModelState.IsValid)
{
return PartialView(model);
}
return Json(new { url = Url.Action("Index") });
}
and inside your AJAX success callback:
success: function(result) {
if (result.url) {
// we have a success
window.location.href = result.url;
} else {
// invalid modelstate => refresh the partial
$('#mydiv').html(result);
}
}
Probably RenderAction should not be used this way.
When using Html.RenderAction, a new/seperate request would be sent to the server. And you got another chance to load some data from db or somewhere else to display to the client. Also, you could apply OutputCache to this action. this is usually the way doing global cache.
Here you are doing a POST to the server. Either directly put a element here or using a partial view to do the Post. And in the corresponding action, do a RedirectToAction.
Do it with ajax or not isn't the point. my opinion is more about the right way using RenderAction

Need Help with MVC workflow - post to another server?

I have a form where I'm collecting contact information (name, address, etc) for an e-commerce app. When the user clicks the Buy button, I want to parse the form, grab a couple of values, and generate a crypto fingerprint.
Then I want to take all of the form values (name, address, etc) from the posted form and redirect that to a new server with the same form values. I may need to inject a few new ones behind the scenes.
No problem capturing the info after the click. I'm just using a Buy action on my controller. The part I can't figure out is posting to the other server with the desired parameters.
[HttpPost]
public ActionResult Buy(BuyModel model)
{
var fingerprint = GenerateFingerprint(.....);
return Redirect("https://some.other.url.com/");
}
EDIT: To clarify. I don't need to just post the data, I actually need to display the response in the browser.
You can use the HttpWebRequest class to make the post to the other server and then redirect as normal.
var httpRequest = (HttpWebRequest)WebRequest.Create("http://example.com/mypage/;
httpRequest.Method = "POST";
httpRequest.ContentType = "application/x-www-form-urlencoded";
For the post data, you'll want to convert this to a byte[] and then add it to a stream.
string postData = "key=value&key2=value2";
byte[] dataArray = Encoding.UTF8.GetBytes(postData);
You'll then be able to set the ContentLength of the request correctly:
httpRequest.ContentLength = dataArray.Length;
Write this to a Stream and we're good to do the post:
using(Stream requestStream = httpRequest.GetRequestStream())
{
requestStream.Write(dataArray, 0, dataArray.Length);
var webResponse = (HttpWebResponse)httpRequest.GetResponse();
}
Assuming you don't need the user to be redirected to the posted page, this will work well. You can look in the webResponse object to find out what happened with the post if that's useful to you.
OK guys, here is what I ended up doing, and it works great.
For the purposes of this discussion, there are 2 views. Membership collects info from the user, and Buy executes the post to the 3rd-party server.
When the user clicks the OK button on the Membership view, the first block of code below gets executed - the HttpPost on the Membership action. Here the info is manipulated if necessary and then saved in TempData so that it can be passed to the Buy view. It then redirects control to the Buy view.
In the Buy action, the second block of code below gets executed. Here, I can grab the stuff collected in Membership and do whatever I need to do. Then copy all of the form data into ViewData[] values. You could use the model instead of ViewData, but I have other stuff in the model. Probably could have combined them, but this works.
Finally, in the Buy view, set all of the form field values using the ViewData values. The key, though is using the jQuery .ready handler so that the form gets submitted automatically when the page is done loading.
So this basically copies the form values from Membership view into the Buy view, adds a few new values and then posts it all to a 3rd-party URL.
BuyController.cs
[HttpPost]
public ActionResult Membership(OtherModel model)
{
// do some stuff with the model
// ...
TempData["OtherModel"] = model;
return RedirectToAction("Buy");
}
...
public ActionResult Buy()
{
// do some stuff to generate crypto key
var fingerprint = DoSomeStuff();
ViewData["x_fp_hash"] = fingerprint;
var otherstuff = (OtherModel) TempData["OtherModel"];
ViewData["x_login"] = otherstuff.login;
return View(model);
}
Buy.cshtml
#{
Layout = "~/Views/Shared/_LayoutBlank.cshtml";
ViewBag.Title = "Validating ...";
}
<script type='text/javascript'>
$(function () {
$("form#simForm").submit();
});
</script>
#model My.Namespace.MyModel
<form id="simForm" runat="server" method='post' action='https://xxxxxx.net/url/transact.dll'>
<input type='hidden' runat="server" name='x_login' id='x_login' value="#ViewData["x_login"]" />
<input type='hidden' runat="server" name='x_fp_hash' id='x_fp_hash'value="#ViewData["x_fp_hash"]"/>
etc.
</form>
The only way you can post the values to another url and redirect in the same post (that is, return the user to the resulting page of the post) is to actually make the users browser do the post. You're also doing the post to an HTTPS, so the whole point is to prevent man-in-the-middle kinds of things.
The only real way to do this is to return a new form to the user, and programmatically tell the browser to submit it using javascript with the new values.

Resources