ApiController returns 404 when ID contains period - asp.net-mvc

I have an ApiController and I want to use email addresses as the ID parameter for requests:
// GET api/employees/email#address.com
public CompactEmployee Get(string id) {
var email = id;
return GetEmployeeByEmail(email);
}
However, I cannot get this to work (returns 404):
http://localhost:1080/api/employees/employee#company.com
The following all work:
http://localhost:1080/api/employees/employee#company
http://localhost:1080/api/employees/employee#company.
http://localhost:1080/api/employees?id=employee#company.com
I have set relaxedUrlToFileSystemMapping="true" in my web.config as detailed by Phil Haack.
I would very much love the full email address to work, but any time the period is followed by any other character, the request returns a 404. Any help would be greatly appreciated!
Solution
Due to a lack of other options, I've headed in the direction Maggie suggested and used the answer from this question to create a rewrite rule to automatically append a trailing slash when I need an email in the URL.
<system.webServer>
....
<rewrite>
<rules>
<rule name="Add trailing slash" stopProcessing="true">
<match url="^(api/employees/.*\.[a-z]{2,4})$" />
<action type="Rewrite" url="{R:1}/" />
</rule>
</rules>
</rewrite>
</system.webServer>

Would adding a trailing slash work for your scenario?
http://localhost:33021/api/employees/employee#company.com/

Check your IIS settings:
Home Directory -> Configuration
Edit the .aspx application extension and ensure that the setting Verify that file exists is off.
UPDATE
I've just tested with a default MVC4 Web API project
URL: http://localhost:10983/api/values/cool#email.com
Action in ValuesController:
public string Get(string id)
{
return id;
}
This was the response:
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">cool#email.com</string>

This is what worked for me:
I was running on targetFramework = 4.6.1. I have upgraded to 4.6.2 and added this in web.config:
<system.web>
<customErrors mode="Off"/>
<compilation debug="true" targetFramework="4.6.2"/>
<!-- This will allow to search for stuff that contains . & etc.-->
<httpRuntime targetFramework="4.6.2" maxRequestLength="100000" maxUrlLength="2048" relaxedUrlToFileSystemMapping="true" requestPathInvalidCharacters=""/>
</system.web>
The requestPathInvalidCharacters="" is to be able to have stuff like & etc in URI, in encoded form, of course.

Related

Unable to map route for robots.txt in asp.net mvc

I am developing an asp.net mvc application. I am creating robots.txt for my application to prevent from bots because my current site is getting many robot requests. So I found this link, Robots.txt file in MVC.NET 4 to create robots.txt. But I when I access my application like this entering url, "www.domain.com/robots.txt", it is always returning 404 page.
This is my action method in HomeController
public ActionResult Robots()
{
Response.ContentType = "text/plain";
return View();
}
This is my robots view
#{
Layout = null;
}
User-agent:*
Disallow:/
I configured route for robots.txt like this in RouteConfig
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.MapMvcAttributeRoutes();
routes.MapRoute(
"Robots.txt",
"robots.txt",
new { controller = "Home", action = "Robots" },
new string[] { "AyarDirectory.Web.Controllers" }
);
//other routes
}
But when I access this url, "www.domain.com/robots.txt", it is always returning 404 page. How can I add robots.txt correctly to my application?
Creating a route ending with a file extension is not allowed by default in ASP.NET MVC. To get around this security restriction, you need to add the following to the Web.config file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- ...Omitted -->
<system.webServer>
<!-- ...Omitted -->
<handlers>
<!-- ...Omitted -->
<add name="RobotsText"
path="robots.txt"
verb="GET"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
</configuration>
In Asp.net Core you can simply add your robots.txt file to the wwwroot directory.
Would say same thing as above you need to change the web.config to allow the route with .txt to work. I had same issue with a project I was working on and I got it to work.
However if you using a view for the output of the robots without a model you might as well keep a static robots.txt as it will give you no advantage. Another way is to output the text direct from the action using a string builder.
Nkosi's System.Web.Handlers.TransferRequestHandler approach in web.config approach did not work for me due to the environment I was working in, resulting in 500 errors.
An alternative of using a web.config url rewrite rule worked for me instead:
<rewrite>
<rules>
<rule name="Dynamic robots.txt" stopProcessing="true">
<match url="robots.txt" />
<action type="Rewrite" url="/DynamicFiles/RobotsTxt" />
</rule>
</rules>
</rewrite>

Incorrect redirect from ASP.NET MVC application under URL Rewrite

I have the next configuration of websites in the IIS:
http://main.domain.com website (ASP.NET MVC 5 app, website binded to the specified host, port 80)
default website binded to :80 with ARR/URL Rewrite module enabled
A wildcard binding *.domain.com is specified in DNS settings.
The desired behavior is to have http://main.domain.com as an entry point and a set of dynamic user-subdomains like http://user1.domain.com, http://user2.domain.com, etc.
Now this behavior is simulated using links like http://main.domain.com/user/user1
I have set up the URL Rewrite rule for main.domain.com in the way like this:
<rule name="user-redirection" enabled="true" stopProcessing="true">
<match url="^.*$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="true">
<add input="{HTTP_HOST}" pattern="^([\w\d-]+)\.domain\.com$" />
</conditions>
<action type="Rewrite" url="http://main.domain.com/user/{C:1}/{R:0}" logRewrittenUrl="true" />
</rule>
Everything is ok here - I can see that http://user1.domain.com works just like http://main.domain.com/user/user1 as it worked earlier.
Then I try to re-implement a logic of checking the existance of specified user in the database. For instance, when user47 doesn't exist - opening of the http://main.domain.com/user/user47 link leads to redirection to the http://main.domain.com entry point.
In the code-side it is done by adding custom filter attribute to the controller action that implements the needed conditional redirect. I have the next code:
public class UserController : Controller {
[CustomRedirectBehavior]
public ActionResult Index()
{
...
}
}
public class CustomRedirectBehaviorAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
...
if (redirectingCondition) {
filterContext.Result = new RedirectResult("http://main.domain.com");
}
}
}
And here.... I get a cyclic redirection error in the browser! To be certain - I have double-checked this behavior:
When I open http://main.domain.com/user/user47 I'm properly redirected to http://main.domain.com
When I open http://user47.domain.com/ I get a cyclic redirection error.
Then in order to investigate the problem I've modified redirect callback to:
filterContext.Result = new RedirectResult("http://someotherdomain.com/some/other/path");
And.... I can see that:
When I open http://main.domain.com/user/user47 I'm properly redirected to http://someotherdomain.com/some/other/path
When I open http://user47.domain.com/ I'm redirected to http://user47.domain.com/some/other/path !!! (And yes, that's not a typo and I've also double-checked this behavior)
So. I need an idea on how to pass through this problem
I've tried to reproduce your case and it seems you are really getting a redirecting loop. So I added a negative lookahead group ((?!main)) into the pattern and it works for me. This is how my web.config looks:
<rule name="user-redirection" enabled="true" stopProcessing="true">
<match url="^.*$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="true">
<add input="{HTTP_HOST}" pattern="^(?!main)([\w\d-]+)\.domain\.com$" />
</conditions>
<action type="Rewrite" url="http://main.domain.com/user/{C:1}" logRewrittenUrl="true" />
</rule>
In order to investigate your problem I've tried:
Disable browser cache. If your browser using cache it might you will not see every change of the rule you made.
Change the type of action to be "Redirect" instead of "Rewrite" and watching what's going on in the "Network" tab in the "Developer tools (F12)".
e.g. <action type="Redirect" ...
Well... Finally I got the answer.
The problem was coming from the Application Request Routing (ARR) module being installed in order to make rewriting work with custom subdomains.
The Reverse rewrite host in response headers option was set to true by default after enabling proxy in the Server Proxy Settings area. Disabling this setting makes redirect working correctly under subdomain-based rewriting.

How to remove/disable mywebsite.azurewebsites.net domain after adding custom domain?

I have created a website on Azure, and linked it with custom domain name(CNET).
Now, when I look at domains on website configuration on Azure panel I see both www.mywebsite.com , and default mywebsite.azurewebsites.net. Both of these domains work fine and I can access website using any of these.
How can I remove mywebsite.azurewebsites.net domain? Does having both of these domains affect SEO?
EDIT*
Thanks for answers, I am trying to enable a 301 redirect, but it is not working. I have added this to web.config file ("example" being my actual site name)
<system.webServer>
<rewrite>
<rules>
<rule name="SEOAzureRewrite" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern="^example.azurewebsites.net$" />
</conditions>
<action type="Redirect" url="http://www.example.com/{R:0}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
I run the website but nothing happens. I can still access the mysite.azurewebsites.net address.
there is no problem in having both domains pointing to your website.
for SEO, you must do 301 redirect from mywebsite.azurewebsites.net to www.mywebsite.com.
It has to be 301 redirect. As this will tell the search engines to always index www.mywebsite.com
Since I couldn't set up the url rewrite in web.config, I created global filter to check for azure url and display error if so. Here is the filter, and I added a new view in my error pages for this purpose that just says "Page doesn't exist" to avoid indexing by search engines. Think this will solve possible duplicate indexing issues.
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.RequestContext.HttpContext.Request.Url != null)
{
string path = context.RequestContext.HttpContext.Request.Url.AbsoluteUri;
if (path.ToLower().Contains("mywebsite.azurewebsites") && !path.ToLower().Contains("error/oldazuresubdomainredirect650"))
{
throw new HttpException(650, "Azure legacy");
}
}
}
You can create a url rewrite rule to do exactly that
You just need to add a redirect rule to your site’s web.config file. You can do that by adding the following rewrite rule to the web.config file in your wwwroot folder. If you don’t have a web.config file, then you can create one and just paste the text below into it, just change the host names to match your site’s host names:
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Redirect rquests to default azure websites domain" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAny">
<add input="{HTTP_HOST}" pattern="^yoursite\.azurewebsites\.net$" />
</conditions>
<action type="Redirect" url="http://www.yoursite.com/{R:0}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
The above is all you need to do to get it to work. If you'd like to better understand what exactly that code does and why it works, there's a detailed writeup at https://zainrizvi.io/blog/block-default-azure-websites-domain/

MVC RequireHttps Attribute is worthless?

I have a requirement to allow users to change their passwords via a form in my asp.net MVC application. My first thought was to decorate the ChangePassword action with a RequireHttps attribute.
However, I still have to send the password unencrypted before the attribute kicks in and returns "The requested resource can only be accessed via SSL". This defeats the purpose, doesn't it?
I am sure I am just confused and RequireHttps is useful; I would like to know if there is a way to use RequireHttps to achieve my aim. Alternatively, I would like to know any other way to achieve it.
UPDATE:
I now have some options thanks to the answers below - I can load the password inputs in an iframe using https, meaning that any posts from it will be encrypted. Other wise I can set the protocol to https in the code that constructs the post url:
var url = '#Url.Action("changePassword", "Security", new { area = "" }, "https")'
I'm not sure which is better, but I'm going to try the second one - any comments welcome.
Your application cannot control whether SSL is enabled. This depends only on web server configuration. The only thing you can do is make sure your application does not trust data that was not encrypted on the wire. RequireHttps does just that. Actions decorated with this attribute will never processes data that was sent in plain text.
The real use case of the RequireHttpsAttribute is to enforce the https:// scheme only when authentication is requested. Not in all cases. The RequireHttpsAttribute only implements the OnAuthentication method of the IAuthenticationFilter interface.
As the OnAuthentication method is only called from within the InvokeAuthenticationFilters method, I wouldn't use the RequireHttpsAttribute attribute.
To properly enforce https:// on certain controllers or actions, I'd create my own attribute, based on ActionFilterAttribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class EnforceHttpsActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (new[] { "GET", "HEAD" }.Any(verb => String.Equals(filterContext.HttpContext.Request.HttpMethod, verb, StringComparison.OrdinalIgnoreCase))) ;
{
string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectResult(url);
}
}
}
To enforce https:// for the whole site, you can get inspired by a web.config markup that I've used for *.azurewebsites.net instances of our sample app.
<system.webServer>
<rewrite>
<rules>
<rule name="HTTPS Redirect in Azure">
<match url="(.+)" />
<conditions>
<add input="{HTTPS}" pattern="^OFF$" />
<add input="{HTTP_HOST}" pattern="^(.+)\.azurewebsites.net(.*)$" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" appendQueryString="true" redirectType="SeeOther" />
</rule>
</rules>
</rewrite>
</system.webServer>
Note: The [RequireHttps] attribute does not handle HEAD requests - and instead gives the exception, so certain spiders or pre-fetching tools will get an error if they attempt to hit your site.
It is best anyway to do something like this in IIS with the rewrite module.
<rule name="Redirect to http" enabled="true" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" negate="false" />
<conditions logicalGrouping="MatchAny">
<add input="{HTTPS}" pattern="off" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" redirectType="Found" />
</rule>
Taken from here: https://blogs.technet.microsoft.com/dawiese/2016/06/07/redirect-from-http-to-https-using-the-iis-url-rewrite-module/
Important Tip: Don't forget to reinstall the rewrite module when migrating to a new server - the error you get if you forget is somewhat obtuse!

IIS 7.5 URL Rewrite not working? help please

I am trying to get a URL rewrite so it takes a URL for e.g my.site.com and rewrites it to "Http://localhost:8080". I keep getting default homepage instead of it being redirected. I have configured the rewrite as follows :
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="mysite">
<match url="(my.+)" />
<action type="Rewrite" url="http://localhost:8080" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
I am at a loss, is there something I am doing wrong ???
Any help will be greatly appreciated.
Cheers
Jeff
It is not 100% clear what you want to achieve and why, but because localhost is involved, it seems you do not want to rewrite but maybe perform a http redirect. Rewriting means that your request URL is sort of changed into another one, like index_foo.htm could be rewritten to index.aspx?what=foo.
But you want to change the domains, so you need to redirect. Try:
<system.webServer>
<httpRedirect enabled="true" destination="http://localhost:8080" childOnly="false" />
</system.webServer>
This will keep whatever local page you have called, i. e. my.site.com/foo.htm will be redirected to localhost:8080/foo.htm.
Because this looks like a temporary thing for development purposes, you can add
httpResponseStatus="Temporary"
to httpRedirect (may be important in order not to lose search engine rankings).

Resources