Passing an url as parameter to controller action - asp.net-mvc

On my asp.net mvc page I need to render multiple images that are stored on third-party images hosting service. The service has some api which returns me a list of image urls.
In *.cshtml file a have the fallowing html markup:
#foreach (var img in Model.Images)
{
<img src="#img.ImageUrl" />
}
It's works perfectly. But for some reason I can't use direct URL to the image in the "src" attribute. Next, I have created new async controller that should return an image:
public class ImagesController : AsyncController
{
//
// GET: /Images/
public void GetImageAsync(string url)
{
AsyncManager.OutstandingOperations.Increment();
WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
AsyncManager.Parameters["response"] = response;
AsyncManager.OutstandingOperations.Decrement();
}
public FileResult GetImageCompleted(WebResponse response)
{
return base.File(response.GetResponseStream(), response.ContentType);
}
}
Now I need to pass a full image url to my controller action.
Something like this:
#foreach (var img in Model.Images)
{
<img src="Images/GetImage/**#img.ImageUrl**" />
}
how to create a new route for passing this parameter to action?
Thanks!

You mean this?
routes.MapRoute(
"GetImage",
"Images/GetImage/{*url}",
new { controller = "Images", action = "GetImageAsync" }
);
I'm not sure why you need all that though. An AsyncController + routing in order to fetch all image url's seems to be overkill...

Related

Redirect to specified action if requested action was not found

How can I redirect Action which is not found in controller into another action within the same controller? Let's say that file abc.txt is requested via http://localhost:5000/Link/GetFile/abc.txt. My controller correctly serving that file. But now, i need to handle request such as http://localhost:5000/Link/Document/abc. Of course there is no any action matched to Document so I need to invoke function Error within the same controller (including id from original request).
I tried to solve this with StatusCodePagesWithReExecute function but then my File action is not working (each request goes directly to Error function).
I have following controller:
public class LinkController : ControllerBase
{
public IActionResult GetFile(string id)
{
return DownloadFile(id);
}
public IActionResult Error(string id)
{
return File("~/index.html", "text/html");
}
private FileResult DownloadFile(string fileName)
{
IFileProvider provider = new PhysicalFileProvider(#mypath);
IFileInfo fileInfo = provider.GetFileInfo(fileName);
var readStream = fileInfo.CreateReadStream();
return File(readStream, "text/plain");
}
}
and startup configuration:
app.UseDefaultFiles();
app.UseStaticFiles(new StaticFileOptions
{
ServeUnknownFileTypes = true,
DefaultContentType = "application/octet-stream",
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}"
);
});
Any clues how to solve this problem?
Regards
You can use UseStatusCodePages to achieve a simple redirection whenever there's a 404. Here's what it looks like:
app.UseStatusCodePages(ctx =>
{
if (ctx.HttpContext.Response.StatusCode == 404)
ctx.HttpContext.Response.Redirect("/Path/To/Your/Action");
return Task.CompletedTask;
});
Just add this somewhere above UseMvc.
EDIT:
I´m sorry, my first answer was not correct.
IRouteCollection router = RouteData.Routers.OfType<IRouteCollection>().First();
with this, you can match an url to controller action
Create HttpContext for testing (example with injection)
private readonly IHttpContextFactory _httpContextFactory;
public HomeController(
IHttpContextFactory httpContextFactory)
{
_httpContextFactory = httpContextFactory;
}
Create the context with values
HttpContext context = _httpContextFactory.Create(HttpContext.Features);
context.Request.Path = "/Home/Index";
context.Request.Method = "GET";
Check route
var routeContext = new RouteContext(context);
await router.RouteAsync(routeContext);
bool exists = routeContext.Handler != null;
Further reading: https://joonasw.net/view/find-out-if-url-matches-action

ASP.NET MVC 5 routing, hide data in URL

I have two types of functionality for a action with a bool variable.
[HttpGet]
public ActionResult action(bool data = false)
{
if(data == false)
{
return View("view1");
}
else
{
return View("view2");
}
}
It is a [httpGet] method. some link has data bool value as true and some has false.
The url has the attribute like http://localhost:58241/action?data=False
I want to hide the ?data=False or ?data=Truefrom URL and should possess all the same functionality like before.
I want the URL like http://localhost:58241/action
Thanks in advance.
Routing has absolutely nothing at all to do with query string parameters. And in any case, you still need to transfer the data parameter to the server for the action method to receive it. There are 3 ways to do this:
Pass it in the URL using HTTP GET (as a route value or query string value)
Pass it in the form body using HTTP POST
Pass it in a model (using HTTP POST and model binding)
The simplest option is #1, however since you mentioned this isn't acceptable to pass the data through the URL, your only choice is to use HTTP post. So, the rest of this answer uses #2.
First, the default route does not cover your choice of URL (/action), so you need to insert a custom route for this.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Add route to handle /action
routes.MapRoute(
name: "Action",
url: "action",
defaults: new { controller = "Data", action = "Action" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Next, you need a controller to handle both the GET and POST from the browser.
public class DataController : Controller
{
public ActionResult Action()
{
return View();
}
[HttpPost]
public ActionResult Action(bool data = false)
{
if (data)
{
return View("view2");
}
return View("view1");
}
}
The data is sent back to the server in the POST, so it is not required to pass it in the URL.
Finally, you have the view (named Action.cshtml) that is returned from the Action action method. It has 2 different form tags that submit a different value for data depending on the button clicked.
#{
ViewBag.Title = "Action";
}
<h2>Choose an Option</h2>
#using (Html.BeginForm("action", "Data")) {
<input type="hidden" name="data" value="true" />
<input type="submit" value="With Data" />
}
#using (Html.BeginForm("action", "Data")) {
<input type="hidden" name="data" value="false" />
<input type="submit" value="Without Data" />
}
Note that you could do this step entirely in JavaScript (AJAX POST) if you please, which would enable you to use a hyperlink instead of a button or you could just style the button using CSS to look like a hyperlink.
You can achieve this functionality partially by making the parameter optional as suggested by #VishalSuthar. But for one condition you must use the parameter if you want to access it using a GET request.
Else, if you make the Action only accessible by POST requests, this part will be easier to implement. In that case only change you need is :
[HttpPost]
public ActionResult action(bool data = false)
{
if(data == false)
{
return View("view1");
}
else
{
return View("view2");
}
}
This way you can pass the parameter in form and hit the action with the URL showing: http://localhost:58241/action
Note: This way the action will not be accessible via GET requests.

Get both image and tooltip in one call

How can I get both the image and tooltip in a single call to a MVC controller method? I can use something like the following to get the image, but how to also get the associated tooltip? The use case is to display an image if the user is allowed to see the image, else display a generic image and a tooltip indicating why the image is not being shown.
To clarify, I would like to avoid two calls to the controller, once to get the image path and tooltip, and another to get the image file. Not only will this result in two round trips across the network, it would also repeat the validation checks. The problem is that the img src call only accommodates the image, not other properties such as the title associated with the image.
<img src="#Url.Action("GetPicture", "User", new { userId = Model.User.Id })" />
Can't you just have a second method for GetTitle using the same permission logic from GetImage and return the appropriate text for each user? Then call this method for the title attribute.
Can you approach the problem in the following way.
Create Model
public class ImageViewModel
{
public string ImagePath
{
get;
set;
}
public string ImageTitle
{
get;
set;
}
}
Create a partial View
#using StackOverFlowProject.Models
#model ImageViewModel
<img src=#Model.ImagePath title=#Model.ImageTitle />
Your Controller
public PartialViewResult _Image(string userID)
{
ImageViewModel model = new ImageViewModel();
//Here You can check what image and tooltip you want show to the user
//model = //FillData from DB /
return PartialView("_Image", model);
}
and at last in Your MainView where you want to display the Image render the partial view
Not sure if this is the best way to do it, but I did it by creating an Ajax form, submitting it using jQuery, returning a JSON object with the byte array encoded as a Base64 string, and using Javascript to display the image. Seems to be working so far, will know more from further tests.
In the view:
<div id="imgDiv">
#using (Ajax.BeginForm("GetImg", "User", null, new AjaxOptions()
{
HttpMethod = "POST",
Url = Url.Action("GetImg", "User"),
OnSuccess = "DisplayImageWithTooltip(data, 'imgDiv')",
}, new { id = "ImgForm", #class = "imageGetterWithTooltip" }))
{
#Html.AntiForgeryToken()
#Html.Hidden("userId", #Model.User.Id)
}
</div>
Javascript to submit form:
$(".imageGetterWithTooltip").submit();
In Controller (based on https://stackoverflow.com/a/9464137/1385857)
return Json(new
{
fileBytes = Convert.ToBase64String(<File byte[]>),
fileType = <FileType>,
tooltip = <toolTip>
}, JsonRequestBehavior.AllowGet);
Javascript to display image
function DisplayImageWithTooltip(data, target) {
var oImg = document.createElement("img");
oImg.width = 150;
oImg.height = 150;
oImg.setAttribute('src', "data:" + data.fileType + ";base64," + data.fileBytes);
oImg.setAttribute('title', data.tooltip);
document.getElementById(target).appendChild(oImg);
}
Using Manish's ideas, my simplified solution is to create a partial view and supply it the image data directly:
Controller:
vmMiniData data = new Models.vmMiniData();
byte[] byteArray = Users.GetPersonnelImage(personnelID);
if (byteArray != null)
{
data.ImageStr = Convert.ToBase64String(byteArray);
}
else
{
data.ImageStr = Convert.ToBase64String(Users.GetPersonnelImage("00000000-0000-0000-0000-000000000000")); //get blank image
}
data.CaptionStr = Users.GetUserJobTitle(personnelID);
return PartialView("Personnel/MiniPersonnelPartial", data);
Model:
public static byte[] GetPersonnelImage(string personnelID)
{
byte[] img = (byte[])(from record in db.PersonnelImages
.Where(R => R.PersonnelID == new Guid(personnelID))
select record.Image).FirstOrDefault();
return img;
}
Then in the partial:
#model vmMiniData
<div>
<div>#Model.CaptionStr</div>
<div> <img src="data:image;base64,#Model.ImageStr" style="width:60px;min-height:30px;" /></div>
</div>
It works very well in MVC 5 :).

Redirect with ASP.NET MVC MapRoute

On my site, I have moved some images from one folder to another.
Now, when I receive a request for old images '/old_folder/images/*' I want to make a permanent redirect to new folder with these images '/new_folder/images/*'
For example:
/old_folder/images/image1.png => /new_folder/images/image1.png
/old_folder/images/image2.jpg => /new_folder/images/image2.jpg
I have added a simple redirect controller
public class RedirectController : Controller
{
public ActionResult Index(string path)
{
return RedirectPermanent(path);
}
}
Now I need to setup proper routing, but I don't know how to pass the path part to the path parameter.
routes.MapRoute("ImagesFix", "/old_folder/images/{*pathInfo}", new { controller = "Redirect", action = "Index", path="/upload/images/????" });
Thanks
I would do in next way
routes.MapRoute("ImagesFix", "/old_folder/images/{path}", new { controller = "Redirect", action = "Index" });
and in controller like that
public class RedirectController : Controller
{
public ActionResult Index(string path)
{
return RedirectPermanent("/upload/images/" + path);
}
}
first download and install RouteMagic package from this link , then redirect your old address to the new address Like the below code :
var NewPath = routes.MapRoute("new", "new_folder/images/{controller}/{action}");
var OldPath = routes.MapRoute("new", "old_folder/images/{controller}/{action}");
routes.Redirect(OldPath ).To(NewPath );
for more information please check out the following link
Redirecting Routes To Maintain Persistent URLs
Answer above using RouteMagic is a good idea, but the example code is wrong (it's included in Phil's post as a bad example).
From the RouteMagic Github demo site global.asax.cs:
// Redirect From Old Route to New route
var targetRoute = routes.Map("target", "yo/{id}/{action}", new { controller = "Home" });
routes.Redirect(r => r.MapRoute("legacy", "foo/{id}/baz/{action}")).To(targetRoute, new { id = "123", action = "index" });
If you specify two routes, you will be setting up an extra mapping that will catch URLs which you don't want.

ASP.NET MVC Form Submission

I'm having a bit of a brain fart; it must be Monday...
I have an MVC form, which allow the user to submit an image.
The image is saved to a folder, then I want to redirect to another Controller and Action to display the image.
What are my options for passing the image name and path back to the controller action to display the graphic?
// Handles the updload, contains a control (ascx)
// and the control's action method is in another controller
public ActionResult Index()
{
return View();
}
// I want this page to display the image uploaded in the Upload.ascx control
// that is in the index method above:
public ActionResult Result()
{
ViewData["MyImage"] = ???
}
Thanks much.
Where is the image being stored? In your content area or in a database? If it's in a database, then I'd construct the controller/action url to display that image form the db. If it's in your content area, then you can construct the url based on the name of the uploaded file. I'd probably create a model rather than passing the url in view data, but view data is a valid (i.e., it works) alternative.
public ActionResult Result( int id ) // db storage
{
return View( new UploadModel
{
ImageUrl = Url.Action( "display", "image", new { id = id }
} );
}
public ActionResult Result() // content area
{
var imageName = ... get image name from ??? ...
return View( new UploadModel
{
ImageUrl = Url.Content( "~/content/images/uploads/" + iamgeName );
});
}

Resources