I am using MVC 3, i am passing some url in the string type property,
for this i have used
var uri = new UrlHelper().Action("ActionName", "ControllerName");
But it is giving error. which is
{"Value cannot be null.\r\nParameter name: routeCollection"}
i know .Action(actionName, controllerName, routeValues, scheme); has four parameters but i only want to pass two, what will be the default value for it??
If you're inside your Controller, you use the static Url.Action()instead, and it will work:
var uri = Url.Action("ActionName", "ControllerName");
And if you're outside of a Controller(e.g in your Model) you have to use the UrlHelper as you did, but passing a Context in parameter, so the method can make the correct url for you.
You can get the request context in this way:
HttpContext.Current.Request.RequestContext
So, if you can use it like this:
UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);
var uri = url.Action("ActionName", "ControllerName");
i searched it and found the answer.
Same Question
var uri = new UrlHelper(HttpContext.Current.Request.RequestContext).Action("ActionName", "ControllerName");
It is working fine after adding HttpContext.Current.Request.RequestContext.
Related
I have seen some very helpful posts about testing Microsoft's routing. One in particular www.strathweb.com/2012/08/testing-routes-in-asp-net-web-api/ seems to deal just with WebApi. Though similiar they are not the same. If I have an MVC application how do I see the method that will be invoked for a given URL. It seems to boils down to creating a 'Request' that can be passed to the constructor of HttpControllerContext and obtaining a reference to the 'current' config (like HttpConfiguration) in testing. Ideas?
Thank you.
Testing Incoming URL
If you need to test routes, you need to mock three classes from the MVC Framework: HttpRequestBase, HttpContextBase and HttpResponseBase(only for outgoing URL´s)
private HttpContextBase CreateHttpContext(string targetUrl = null, string httpMethod = "GET")
{
// create mock request
Mock<HttpRequestBase> mockRequest = new Mock<HttpRequestBase>();
// url you want to test through the property
mockRequest.Setup(m => m.AppRelativeCurrentExecutionFilePath).Returns(targetUrl);
mockRequest.Setup(m => m.HttpMethod).Returns(httpMethod);
// create mock response
Mock<HttpResponseBase> mockResponse = new Mock<HttpResponseBase>();
mockResponse.Setup(m => m.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(s => s);
// create the mock context, using the request and response
Mock<HttpContextBase> mockContext = new Mock<HttpContextBase>();
mockContext.Setup(m => m.Request).Returns(mockRequest.Object);
mockContext.Setup(m => m.Response).Returns(mockResponse.Object);
// return the mock context object
return mockContext.Object;
}
then you need an additional helper method that let´s you specify the URL to test and the expected segment variables and an object for additional variables.
private void TestRouteMatch(string url, string controller, string action,
object routeProperties = null, string httpMethod = "GET")
{
// arrange
RouteCollection routes = new RouteCollection();
// loading the defined routes about the Route-Config
RouteConfig.RegisterRoutes(routes);
RouteData result = routes.GetRouteData(CreateHttpContext(url, httpMethod));
// assert
Assert.IsNotNull(result);
// here you can check your properties (controller, action, routeProperties) with the result
Assert.IsTrue(.....);
}
You don´t need to define your routes in the test methodes, because they were load directly using the RegisterRoutes method in the RouteConfig class.
The mechanism by wich inbound URL matching works.
GetRouteData(HttpContextBase httpContext)
referencesource.microsoft
The framework calls this method for each route table entry, until one of thems returns a non-null value.
You have to call the helper method as example in this way
[TestMethod]
public void TestIncomingRoutes() {
// check for the URL that is hoped for
TestRouteMatch("~/Home/Index", "Home", "Index");
}
the method check the URL you expecting as in the example above, call the Index action in the Home controller. You must prefix the URL with tilde (~) this is they way how the ASP.NET Framework presents the URL to the routing system.
In reference to the book Pro ASP.NET MVC 5 by Adam Freeman i can recommand it to every ASP.NET MVC developer!
User can download price information PDFs located in a folder PriceInformations with subfolders specifying the document type, e.g.:
/PriceInformations/Clothes/Shoes.pdf
/PriceInformations/Clothes/Shirts.pdf
/PriceInformations/Toys/Games.pdf
/PriceInformations/Toys/Balls.pdf
Consider following action in Controller Document to download those PDFs:
// Filepath must be like 'Clothes\Shoes.pdf'
public ActionResult DownloadPDF(string filepath)
{
string fullPath = Path.Combine(MyApplicationPath, filepath);
FileStream fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read);
return base.File(fileStream, "application/pdf");
}
To get a PDF document, my client wants URLs to be like:
/PriceInformations/Clothes/Shoes.pdf
I could easily create an overload function for this case:
public ActionResult DownloadPDF(string folder, string filename)
{
return this.DownloadPDF(Path.Combine(folder, filename);
}
And map it like
routes.MapRoute(
"DownloadPriceInformations",
"DownloadPriceInformations/{folder}/{filename}",
new
{
controller = "Document",
action = "DownloadPDF"
});
But I'm curious if it would be possible to work without an overload function and to map this case in RegisterRoutes in Global.asax, so to be able to create one single parameter out of of multiple parameters:
routes.MapRoute(
"DownloadPriceInformations",
"DownloadPriceInformations/{folder}/{filename}",
new
{
controller = "Document",
action = "DownloadPDF",
// How to procede here to have a parameter like 'folder\filename'
filepath = "{folder}\\{filename}"
});
Question became a bit longer but I wanted to make sure, you get my desired result.
Sorry, this is not supported in ASP.NET routing. If you want multiple parameters in the route definition you'll have to add some code to the controller action to combine the folder and path name.
An alternative is to use a catch-all route:
routes.MapRoute(
"DownloadPriceInformations",
"DownloadPriceInformations/{*folderAndFile}",
new
{
controller = "Document",
action = "DownloadPDF"
});
And the special {*folderAndFile} parameter will contain everything after the initial static text, including all the "/" characters (if any). You can then take in that parameter in your action method and it'll be a path like "clothes/shirts.pdf".
I should also note that from a security perspective you need to be absolutely certain that only allowed paths will be processed. If I pass in /web.config as the parameter, you must make sure that I can't download all your passwords and connection strings that are stored in your web.config file.
There are a lot of material written about Subdomain routing in ASP.NET MVC. Some of them use Areas as target for subdomains other use another Controllers.
There are some of them:
Subdomains for a single application with ASP.NET MVC
Asp.Net MVC 2 Routing SubDomains to Areas
MVC 3 Subdomain Routing
MVC-Subdomain-Routing on Github
They do all explain how to accept and route requests with subdomain.
But:
None of them explains how to generate URLs with subdomain. I.e. I tried #Html.RouteLink("link to SubDomain", "SubdomainRouteName") but what it ignores subdomain and generates url without it
How to deal with the same names of controllers from different areas. All those solutions (they use namespaces for these purpose) throw exception that exist several controllers and suggest using namespaces :)
Purpose:
create mobile version of site using subdomain
I've wrote a post on how I use subdomain routing in my application. The source code is available on the post, but I'll try to explain how I did my custom RouteLink method.
The helper method uses the RouteTable class to get the Route object based on the current Url and cast it to a SubdomainRoute object.
In my case all routes are defined using the SubdomainRoute and everytime I need to add a link to some other page I use my custom RouteLink helper, this is why I consider this cast safe. With the SubdomainRoute variable available I'm able to get the subdomain name and then build the Url using the UriBuilder class.
This is the code I'm currently using.
public static IHtmlString AdvRouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, object routeValues, object htmlAttributes)
{
RouteValueDictionary routeValueDict = new RouteValueDictionary(routeValues);
var request = htmlHelper.ViewContext.RequestContext.HttpContext.Request;
string host = request.IsLocal ? request.Headers["Host"] : request.Url.Host;
if (host.IndexOf(":") >= 0)
host = host.Substring(0, host.IndexOf(":"));
string url = UrlHelper.GenerateUrl(routeName, null, null, routeValueDict, RouteTable.Routes, htmlHelper.ViewContext.RequestContext, false);
var virtualPathData = RouteTable.Routes.GetVirtualPathForArea(htmlHelper.ViewContext.RequestContext, routeName, routeValueDict);
var route = virtualPathData.Route as SubdomainRoute;
string actualSubdomain = SubdomainRoute.GetSubdomain(host);
if (!string.IsNullOrEmpty(actualSubdomain))
host = host.Substring(host.IndexOf(".") + 1);
if (!string.IsNullOrEmpty(route.Subdomain))
host = string.Concat(route.Subdomain, ".", host);
else
host = host.Substring(host.IndexOf(".") + 1);
UriBuilder builder = new UriBuilder(request.Url.Scheme, host, 80, url);
if (request.IsLocal)
builder.Port = request.Url.Port;
url = builder.Uri.ToString();
return htmlHelper.Link(linkText, url, htmlAttributes);
}
private static IHtmlString Link(this HtmlHelper htmlHelper, string text, string url, object htmlAttributes)
{
TagBuilder tag = new TagBuilder("a");
tag.Attributes.Add("href", url);
tag.InnerHtml = text;
tag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
I'd like to create URLs based on the URL used by the client for the active request. Is there anything smarter than taking the current HttpServletRequest object and it's getParameter...() methods to rebuilt the complete URL including (and only) it's GET parameters.
Clarification: If possible I want to resign from using a HttpServletRequest object.
Well there are two methods to access this data easier, but the interface doesn't offer the possibility to get the whole URL with one call. You have to build it manually:
public static String makeUrl(HttpServletRequest request)
{
return request.getRequestURL().toString() + "?" + request.getQueryString();
}
I don't know about a way to do this with any Spring MVC facilities.
If you want to access the current Request without passing it everywhere you will have to add a listener in the web.xml:
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
And then use this to get the request bound to the current Thread:
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()
Instead of using RequestContextHolder directly, you can also use ServletUriComponentsBuilder and its static methods:
ServletUriComponentsBuilder.fromCurrentContextPath()
ServletUriComponentsBuilder.fromCurrentServletMapping()
ServletUriComponentsBuilder.fromCurrentRequestUri()
ServletUriComponentsBuilder.fromCurrentRequest()
They use RequestContextHolder under the hood, but provide additional flexibility to build new URLs using the capabilities of UriComponentsBuilder.
Example:
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentRequestUri();
builder.scheme("https");
builder.replaceQueryParam("someBoolean", false);
URI newUri = builder.build().toUri();
Java's URI Class can help you out of this:
public static String getCurrentUrl(HttpServletRequest request){
URL url = new URL(request.getRequestURL().toString());
String host = url.getHost();
String userInfo = url.getUserInfo();
String scheme = url.getProtocol();
String port = url.getPort();
String path = request.getAttribute("javax.servlet.forward.request_uri");
String query = request.getAttribute("javax.servlet.forward.query_string");
URI uri = new URI(scheme,userInfo,host,port,path,query,null)
return uri.toString();
}
in jsp file:
request.getAttribute("javax.servlet.forward.request_uri")
You can also add a UriComponentsBuilder to the method signature of your controller method. Spring will inject an instance of the builder created from the current request.
#GetMapping
public ResponseEntity<MyResponse> doSomething(UriComponentsBuilder uriComponentsBuilder) {
URI someNewUriBasedOnCurrentRequest = uriComponentsBuilder
.replacePath(null)
.replaceQuery(null)
.pathSegment("some", "new", "path")
.build().toUri();
//...
}
Using the builder you can directly start creating URIs based on the current request e.g. modify path segments.
See also UriComponentsBuilderMethodArgumentResolver
If you need the URL till hostname and not the path use Apache's Common Lib StringUtil, and from URL extract the substring till third indexOf /.
public static String getURL(HttpServletRequest request){
String fullURL = request.getRequestURL().toString();
return fullURL.substring(0,StringUtils.ordinalIndexOf(fullURL, "/", 3));
}
Example: If fullURL is https://example.com/path/after/url/ then
Output will be https://example.com
System.out.println(((HttpServletRequest)request).getRequestURI());
I used it. hope it's useful.
I am using an authentication attribute on some of my actions in an asp.net mvc page to refer people to a login screen if they have not authenticated. My problem is returning them to the referring page after they have logged in. I was just keeping track of the referring action and referring controller but that becomes problematic when I also need to keep track of some parameters. Is there some nifty built in trick about which I don't know?
In case you're using FormsAuthentication, when ASP.NET redirects a user to the login page, the URL looks something like this:
http://www.mysite.com/Login?ReturnUrl=/Something
The login form's action attribute should have the same ReturnUrl parameter (either as hidden input or as part of Url) so that FormsAuthentication can pick it up and redirect, e.g.
<form action="Login?ReturnUrl=<%=Html.AttributeEncode(Request.QueryString["ReturnUrl"]) %>"></form>
or
<form><input type="hidden" name="ReturnUrl" id="ReturnUrl" value="<%=Html.AttributeEncode(Request.QueryString["ReturnUrl"])"%> /></form>
What I did to achieve that result might be overdoing it, and I'd like to see some other methods as well. However, here's my code.
Please note it's using Moq to mock a context... And, I haven't done anything with the querystring yet (my routes don't contain any querystrings).
var urlReferrer = Request.UrlReferrer;
if (urlReferrer != null)
{
var url = "~" + Server.UrlDecode(urlReferrer.PathAndQuery);
// get routecollection
var routeCollection = new RouteCollection();
GlobalApplication.RegisterRoutes(routeCollection);
// mcok context
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
context.Expect(ctx => ctx.Request).Returns(request.Object);
// mock request
// TODO: convert querystring to namevaluecollection
// now it's just stripped
if (url.IndexOf('?') > 0)
{
url = url.Substring(0, url.IndexOf('?'));
}
var mock = Mock.Get(context.Object.Request);
// TODO: insert namevaluecollection of querystring
mock.Expect(req => req.QueryString).Returns(new NameValueCollection());
mock.Expect(req => req.AppRelativeCurrentExecutionFilePath).Returns(url);
mock.Expect(req => req.PathInfo).Returns(string.Empty);
// get routedata with mocked context
var routeData = routeCollection.GetRouteData(context.Object);
var values = routeData.Values;
return RedirectToAction(routeData.Values["action"].ToString(), values);
}
As I said, it's maybe a bit overcomplicated :)
You should always ensure that the referring URL is within your domain and a plausible string that they could be coming from. Otherwise this has the potential of being used with flash or other client side technologies to do things like response splitting or other attacks, known and unknown.
The HTTP referer is user input, and it should be validated like any other.