Legacy PHP urls do not invoke custom routing in MVC - asp.net-mvc

We recently replaced an old php website with an asp.net MVC website. In order to prevent 404 errors from the legacy urls in search engines, we setup a custom legacy route system via - http://www.mikesdotnetting.com/Article/108/Handling-Legacy-URLs-with-ASP.NET-MVC
The code works on my local machine, and it redirects to the correct route; however, the live server issues a 404. Bonus problem/clue, the 404 is not our custom 404 page but the iis 6 default page.
The code:
public class LegacyUrlRoute: RouteBase
{
// source: http://www.mikesdotnetting.com/Article/108/Handling-Legacy-URLs-with-ASP.NET-MVC
public override RouteData GetRouteData(HttpContextBase httpContext)
{
const string status = "301 Moved Permanently";
var request = httpContext.Request;
var response = httpContext.Response;
var legacyUrl = request.Url.ToString();
var newUrl = "";
if (legacyUrl.Contains(".php"))
{
newUrl = "/";
if (legacyUrl.Contains("support/mailfilter.php"))
newUrl = "/support/";
else if (legacyUrl.Contains("/support/default.php"))
newUrl = "/support/";
else if (legacyUrl.Contains("/business/default.php"))
newUrl = "/services/";
else if (legacyUrl.Contains("/residential/default.php"))
newUrl = "/services/";
else if (legacyUrl.Contains("/about/default.php"))
newUrl = "/home/about/";
else if (legacyUrl.Contains("/jobs.php"))
newUrl = "/jobs/";
else if (legacyUrl.Contains("/support/links.php"))
newUrl = "/support/";
else if (legacyUrl.Contains("/support/settings.php"))
newUrl = "/support/";
else if (legacyUrl.Contains("/default.php"))
newUrl = "/";
response.Status = status;
response.RedirectLocation = newUrl;
response.End();
}
return null;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
Note:
We suspect that the problem is with iis being unable to serve php pages, but I can't seem to find a setting in iis to fix the problem. It is as if the request never hits the Controller code, error or otherwise. All other pages/routing is working perfectly.

Found the answer.
The problem is due to the fact that iis never starts the asp.net service if the extension is .php.
The solution is to go to Properties>Home Directory>Configuration, find the .php extension, change the executable path to the asp.net path, and limit it to "GET,HEAD,POST,DEBUG" or whatever you prefer. I originally selected the "All Verbs" radio button, but that did not work.
Though the page did not specifically have the answer I came up with, this page did help.

Related

ASP.net MVC Culture Info in a Cookie

I have just come across a rather strange issue and I really can't understand why this is happening ...
I have a rather simple, MVC website based on .NET Framework 4.7.2. I'm keeping 2 Resource files (resx) for a couple of languages. So far so good. What I do is keeping the selected Culture into a Cookie with the CultureInfo (en-US & el-GR). In my development maching, using IISExpress, everything is running like a charm! The cookie is being updated as expected and of course the value switching can be seen from the browser debugging.
Using Application_BeginRequest() from Global.asax I can recover the selected culture:
protected void Application_BeginRequest(object sender, EventArgs e)
{
string culture = "el-GR";
var langCookie = Request.Cookies["SiderLangCookie"];
if (langCookie != null)
culture = langCookie.Value;
else
{
culture = "el-GR";
HttpCookie cookie = new HttpCookie("SiderLangCookie", culture)
{
HttpOnly = true,
Expires = DateTime.Now.AddMonths(6)
};
Response.AddHeader("Set-Cookie", "SameSite=Strict;Secure");
Response.AppendCookie(cookie);
}
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
}
And later on, if the user chooses to do so, he/she may change the culture from an anchor button:
<a class="socials-item" href="javascript:SwitchLanguage();" title="Language"><i class="fa fa-flag" aria-hidden="true"></i></a>
which is calling a javascript function for an AJAX POST request to the Controller action:
function SwitchLanguage() {
$.ajax({
url: '#Url.Action("SwitchLanguage", "Home")',
method: 'POST',
success: function (response) {
if (response.result == "OK") {
toastr.success("#Resource.LanguageSwitchSuccess", "SUCCESS");
setTimeout(function () { window.location.reload(); }, 2500);
}
},
error: function () {
toastr.error("#Resource.LanguageSwitchError", "ERROR");
}
});
}
and this is my action:
[HttpPost]
public ActionResult SwitchLanguage()
{
string lang = "en-US";
var langCookie = Request.Cookies["SiderLangCookie"];
if (langCookie == null)
{
langCookie = new HttpCookie("SiderLangCookie", lang)
{
HttpOnly = true,
Expires = DateTime.Now.AddMonths(6),
};
Response.AddHeader("Set-Cookie", "SameSite=Strict;Secure");
Response.AppendCookie(langCookie);
}
else
{
lang = langCookie.Value;
if (lang == "en-US")
lang = "el-GR";
else
lang = "en-US";
langCookie.Value = lang;
Response.AddHeader("Set-Cookie", "SameSite=Strict;Secure");
Response.SetCookie(langCookie);
}
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(lang);
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(lang);
return Json(new { result = "OK" }, JsonRequestBehavior.AllowGet);
}
For some reason, when I deploy the website (Publishing to a folder and uploading to the Host), even though the Action code is executing successfully (no exceptions and errors whatsoever), the cookie is no longer updating the value to either el-GR or en-US. It just sticks to the first value it got when first created.
Does anyone have the slightest clue why is this happening?
Thanks in advance.
Cookies issues usually appears if you do not have correct setup the relative sessions on web.config.
Check the domain that is correct setup with out the www. - if you use the www. then check that all calls done from www.
<httpCookies domain="yourdomain.com" httpOnlyCookies="true" requireSSL="true"/>
The domain property also exist on forms and on roleManager
Ok, I managed to figure out the situation. Many many thanks to Thomas Ardal who wrote this article:
[The ultimate guide to secure cookies with web.config in .NET][1]
[1]: https://blog.elmah.io/the-ultimate-guide-to-secure-cookies-with-web-config-in-net/
It seems that the latest changes regarding Cookie management by the browsers are more restrictive than anticipated.
Incorporating the rewrite rule, Thomas proposed, did the trick.
Now the Cookie value seems to be changing as expected.
Thanks

OpenIdConnect Correlation Cookie not found when user click link from Office application

I have an app that is authenticating with Azure Active Directory using OpenIdConnect. Everything is working fine except when I link to my site from a Office Application (excel/ word). From these applications I get a "Exception: Correlation failed.".
From my research it seems to be that office is first doing the 302 redirect and then opening that page not the original link.
See: https://github.com/aspnet/Security/issues/1252
After a recommendation for how to handle this scenario. I don't want to have to make to many changes to the authentication flow and introduce bugs.
I have tried redirecting to a different page on my site when a user-agent of excel is detected. I thought then the correct cookie would be set and I could redirect from there to the requested page which would then trigger authorization. No luck though
OnRedirectToIdentityProvider = context =>
{
if (context.Request.Headers["User-Agent"].ToString().Contains("Microsoft Office Excel"))
{
string redirect = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + "/Home/Office" + "?url=" + context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + context.Request.Path;
context.Response.Clear();
context.Response.Redirect(redirect);
context.HandleResponse();
return Task.CompletedTask;
}
}
I was able to implement a decent solution using owin middleware. Largely with the help of this post: https://github.com/aspnet/AspNetKatana/issues/78
I how ever did need to convert it to .net core 2.0. Here's the converted code:
public class MsOfficeLinkPrefetchMiddleware
{
RequestDelegate _next;
public MsOfficeLinkPrefetchMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
if (Is(context, HttpMethod.Get, HttpMethod.Head) && IsMsOffice(context))
{
// Mitigate by preempting auth challenges to MS Office apps' preflight requests and
// let the real browser start at the original URL and handle all redirects and cookies.
// Success response indicates to Office that the link is OK.
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.Headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
context.Response.Headers["Pragma"] = "no-cache";
context.Response.Headers["Expires"] = "0";
}
else if (_next != null)
{
return _next.Invoke(context);
}
return Task.CompletedTask;
}
private static bool Is(HttpContext context, params HttpMethod[] methods)
{
var requestMethod = context.Request.Method;
return methods.Any(method => StringComparer.OrdinalIgnoreCase.Equals(requestMethod, method.Method));
}
private static readonly Regex _msOfficeUserAgent = new Regex(
#"(^Microsoft Office\b)|([\(;]\s*ms-office\s*[;\)])",
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);
private static bool IsMsOffice(HttpContext context)
{
var headers = context.Request.Headers;
var userAgent = headers["User-Agent"];
return _msOfficeUserAgent.IsMatch(userAgent)
|| !string.IsNullOrWhiteSpace(headers["X-Office-Major-Version"]);
}
}
Startup
app.UseMiddleware<MsOfficeLinkPrefetchMiddleware>();
Hope this is able to help someone in the future.
In Startup.ConfigureServices(), you'll need to add this line
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for
//non -essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});

Setting ContentType causing multiple requests in MVC Web API

I have a simple GetFile Method in a Web API that returns a file stored as binary in a database. The file can be either image, video or a recording, so I set the content type based on the extension:
[System.Web.Mvc.HttpGet]
public HttpResponseMessage GetFile(Guid Id)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
var file = db.Files.Where<NonConformityFile>(item => item.Id == Id).FirstOrDefault<NonConformityFile>();
byte[] imgData = file.FileContents;
MemoryStream ms = new MemoryStream(imgData);
response.Content = new StreamContent(ms);
if (file.FileExtension == "png")
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
else if (file.FileExtension == "mp4")
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("video/mp4");
else if (file.FileExtension == "caf")
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("audio/x-caf");
else
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.UnsupportedMediaType, file.FileExtension + " is not a supported file format"));
return response;
}
This Works fine, but when adding some Security to the Method (by providing a token in the http header) it started crashing. When setting a breakpoint, I discovered that the breakpoint was actually hit twice. And the second time the token was gone from the header, causing authentication failure.
Further debugging made me find out that setting the response.Content.Header.ContentType causes the GetFile Method to be called a second time.
Is this designed behaviour, or some crazy side effects from something in my Development environment?
To reproduce, try this:
[System.Web.Mvc.HttpGet]
public HttpResponseMessage GetFile()
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(new MemoryStream());
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
return response;
}
And see if the Method is run twice. I cannot see the Logic in this behaviour, and it is causing problems in my authentication.

get absolute url on handle ressource request on blackberry

I use this method the get the urls of ressources contain on web page
public InputConnection handleResourceRequest(BrowserFieldRequest request) throws Exception
{
final String url = request.getURL();
return super.handleResourceRequest(request);
}
But, I made request.getURL(); it returns relative url and not the absolute url.
How can I change it to get the absolute URL?
When I run your code, it does return me absolute URLs, even when my web page contained relative links. That said, it wouldn't surprise me if sometimes, it doesn't. I haven't fully tested this code, but I would think you could try something like this.
Basically, you check to see if the URL is absolute, and if not, you assemble an absolute URL by using the parent BrowserField document URL:
ProtocolController controller = new ProtocolController(_browserField) {
public InputConnection handleResourceRequest(BrowserFieldRequest request) throws Exception {
String absoluteUrl = null;
URI uri = URI.create(request.getURL());
if (uri.isAbsolute()) {
absoluteUrl = request.getURL();
} else {
String docUrl = _browserField.getDocumentUrl();
String url = request.getURL();
if (url.startsWith("/")) {
// the URL is relative to the server root
URI docUri = URI.create(docUrl);
absoluteUrl = docUri.getScheme() + "://" + docUri.getHost() + url;
} else {
// the URL is relative to the document URL
absoluteUrl = docUrl + url;
}
}
System.out.println(" requesting: " + absoluteUrl);
return super.handleResourceRequest(request);
}
}
Again, for me, I was getting absolute URLs, so I couldn't easily test the code in the branch where the URL is relative. So, it's possible that I'm dropping a "/" somewhere, or not handling file:/// URLs properly.
But, this is a starting point, to workaround your problem.

EntityClassGenerator : Not generating any output for NorthwindDataService

I am trying to generate the OData Proxy for the service : http://services.odata.org/Northwind/Northwind.svc/$metadata
I am using System.Data.Services.Design.EntityClassGenerator for generating the OData proxy.
When I instantiate the EntityClassGenerator and call GenerateCode the output has no errors. But there is no code in the generated proxy code.
The same code works for my own service. But when I point it to any external service the EntityClassGenerator is not working.
Here is the code :
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(metadataEndpoint);
webRequest.Method = "GET";
webRequest.ContentType = "text/xml;encoding='utf-8";
webRequest.Proxy = (proxy != null) ? proxy : WebRequest.DefaultWebProxy;
using (WebResponse response = webRequest.GetResponse())
{
string xml = string.Empty;
XmlReaderSettings settings = new XmlReaderSettings();
using (TextReader reader = new StreamReader(response.GetResponseStream()))
{
xml = reader.ReadToEnd();
using (XmlTextReader sourceReader = new XmlTextReader(reader))
{
using (StringWriter targetWriter = new StringWriter())
{
// Generate the OData End point proxy.
EntityClassGenerator entityGenerator = new EntityClassGenerator(LanguageOption.GenerateCSharpCode);
entityGenerator.OnPropertyGenerated += new EventHandler<PropertyGeneratedEventArgs>(entityGenerator_OnPropertyGenerated);
IList<System.Data.Metadata.Edm.EdmSchemaError> errors = entityGenerator.GenerateCode(sourceReader, targetWriter, namespacename);
entityGenerator.OnPropertyGenerated -= new EventHandler<PropertyGeneratedEventArgs>(entityGenerator_OnPropertyGenerated);
odataProxyCode = targetWriter.ToString();
}
}
}
}
I found the code in the question to be a useful starting point for doing exactly what the OP was asking. So even though the OP doesn't accept answers, I'll describe the changes I made to get it to work in case it is useful to someone else.
Removed the xml = reader.ReadToEnd(); call. I assume that was for debugging purposes to look at the response from the web request, but it had the result of "emptying" the reader object of the response. That meant that there was nothing left in the reader for the GenerateCode call.
The important one: Changed the use of EntityClassGenerator to System.Data.Services.Design.EntityClassGenerator. In the code below, I included the entire name space for clarity and specificity. Based on the code in the question, it appears the OP was probably using System.Data.Entity.Design.EntityClassGenerator. I used .NET Reflector to examine datasvcutil.exe, which is a command-line utility that can generate the proxy classes. I saw that it referenced the generator in that other name space.
For figuring out the problems, I dumped the errors from the GenerateCode call. One could examine them in the debugger, but some kind of automated checking of them would be needed regardless.
Here is what I ended up with:
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.
Create("http://services.odata.org/Northwind/Northwind.svc/$metadata");
webRequest.Method = "GET";
webRequest.ContentType = "text/xml;encoding='utf-8";
webRequest.Proxy = WebRequest.DefaultWebProxy;
using (WebResponse response = webRequest.GetResponse())
{
using (TextReader reader = new StreamReader(response.GetResponseStream()))
{
using (XmlTextReader sourceReader = new XmlTextReader(reader))
{
using (StringWriter targetWriter = new StringWriter())
{
// Generate the OData End point proxy.
System.Data.Services.Design.EntityClassGenerator entityGenerator =
new System.Data.Services.Design.EntityClassGenerator(
System.Data.Services.Design.LanguageOption.GenerateCSharpCode);
IList<System.Data.Metadata.Edm.EdmSchemaError> errors =
entityGenerator.GenerateCode(sourceReader, targetWriter,
"My.Model.Entities");
foreach (System.Data.Metadata.Edm.EdmSchemaError error in errors)
Console.WriteLine("{0}: {1}", error.Severity.ToString(), error.Message);
string odataProxyCode = targetWriter.ToString();
}
}
}
}

Resources