I'm using angularJS and C#,
Due to my superior demand, i have to use HTML5 mode at all cost, mean no '#' sign.
And Again do to his request, i Used rewrite to provide ability to access the angular page even on page refresh.
Recently we implement phantomJS, the first page did worked well, till i notice other page doesn't, after digin-in in codes, i found out that, NO, it wont capture the whole URL, for example, for: http://localhost:xyzw/flights?_escaped_fragment_= it only capture the http://localhost:1350/?_escaped_fragment_=, which doesn't contain the 'flights' part. i also made some change in my rewrite config, which the last one was adding following rule to ignore these path for angular app, and process them directly: <add input="{REQUEST_URI}" matchType="Pattern" pattern="(.*)_escaped_fragment_=(.*)" ignoreCase="false" />
I separate my code for those who come and say hey shorten your issue, as whole of it can be,...
first part, my configs and small codes
second the tutorial i read about PhantomeJS
other files which may be required
Here are my main setup:
WebConfig Rewrite:
<rewrite>
<rules>
<!--<rule name="Seo rewrite rule" stopProcessing="true">
<conditions>
<add input="{QUERY_STRING}" pattern="(.*)_escaped_fragment_=(.*)" ignoreCase="false" />
</conditions>
<action type="Rewrite" url="http://service.prerender.io/http://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" />
</rule>-->
<rule name="Index Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/api/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Token" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/bundles/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Content/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Scripts/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/SiteMap/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/CallBackBank/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Error/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="(.*)_escaped_fragment_=(.*)" ignoreCase="false" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/HtmlSnapshot[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Flight[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Hotel[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Tour[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/TravelAgency[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Users[^s]?/" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
Phantom JS required Filter:
public class AjaxCrawlableAttribute : System.Web.Mvc.ActionFilterAttribute
{
private const string Fragment = "_escaped_fragment_";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.RequestContext.HttpContext.Request;
var url = request.Url.ToString();
if (request.QueryString[Fragment] != null && !url.Contains("HtmlSnapshot/returnHTML"))
{
url = url.Replace("?_escaped_fragment_=", string.Empty).Replace(request.Url.Scheme + "://", string.Empty);
url = url.Split(':')[1];
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "controller", "HtmlSnapshot" }, { "action", "returnHTML" }, { "url", url } });
}
return;
}
}
Route Config:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LowercaseUrls = true;
//PhantomJS
routes.MapRoute(
name: "HtmlSnapshot",
url: "HtmlSnapshot/returnHTML/{*url}",
defaults: new {controller = "HtmlSnapshot", action = "returnHTML", url = UrlParameter.Optional});
////PhantomJS
//routes.MapRoute(
// name: "SPA",
// url: "{*catchall}",
// defaults: new {controller = "Home", action = "Index"});
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional});
}
PhantomJS for C# The tutorial that i read:
OK, since the tutorial i read about phantomjs is in non-english i post the thing i wrote for my later usage:
1.Install Package
Install-Package PhantomJS.exe -version 1.9.2.1
2.Create Helper
public class AjaxCrawlableAttribute : System.Web.Mvc.ActionFilterAttribute
{
private const string Fragment = "_escaped_fragment_";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.RequestContext.HttpContext.Request;
var url = request.Url.ToString();
if (request.QueryString[Fragment] != null && !url.Contains("HtmlSnapshot/returnHTML"))
{
url = url.Replace("?_escaped_fragment_=", string.Empty).Replace(request.Url.Scheme + "://", string.Empty);
url = url.Split(':')[1];
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "controller", "HtmlSnapshot" }, { "action", "returnHTML" }, { "url", url } });
}
return;
}
}
3.Replace Default Routes With:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "HtmlSnapshot",
url: "HtmlSnapshot/returnHTML/{*url}",
defaults: new { controller = "HtmlSnapshot", action = "returnHTML", url = UrlParameter.Optional });
//If doesn't work, use default route instead...:
routes.MapRoute(
name: "SPA",
url: "{*catchall}",
defaults: new { controller = "Home", action = "Index" });
}
4.Add AjaxCrawlableAttribute As A Filter
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AjaxCrawlableAttribute());
}
}
5.Create HtmlSnapshot Controller
public ActionResult returnHTML(string url)
{
var prefix = HttpContext.Request.Url.Scheme + "://" + HttpContext.Request.Url.Host + ":";
url = prefix + url;
string appRoot = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
var startInfo = new ProcessStartInfo
{
Arguments = string.Format("{0} {1}", "\"" + Path.Combine(appRoot, "Scripts\\seo.js") + "\"", url),
FileName = "\"" + Path.Combine(appRoot, "bin\\phantomjs.exe") + "\"",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
StandardOutputEncoding = System.Text.Encoding.UTF8
};
var p = new Process();
p.StartInfo = startInfo;
p.Start();
string output1 = p.StandardOutput.ReadToEnd();
p.WaitForExit();
var removeNgUiView = output1.Replace("<!-- ngView: -->", "").Replace("ng-view=\"\"", "");
removeNgUiView = Regex.Replace(removeNgUiView, "<!--\\suiView:\\s\\w*\\s-->", "");
removeNgUiView = Regex.Replace(removeNgUiView, "(<\\w+[^>]*)(ui-view(=\"\\w*\")?)([^<]*>)", "$1$4");
removeNgUiView = Regex.Replace(removeNgUiView, "(<\\w+[^>]*)(ng-app(=\"\\w*\")?)([^<]*>)", "$1$4");
ViewData["result"] = removeNgUiView;
return View();
}
6.Create Views of Controller
#{
Layout = null;
}
#Html.Raw(ViewData["result"])
7.Create seo.js in Script (!Important) Folder
var page = require('webpage').create();
var system = require('system');
var lastReceived = new Date().getTime();
var requestCount = 0;
var responseCount = 0;
var requestIds = [];
var startTime = new Date().getTime();;
page.onResourceReceived = function (response) {
if (requestIds.indexOf(response.id) !== -1) {
lastReceived = new Date().getTime();
responseCount++;
requestIds[requestIds.indexOf(response.id)] = null;
}
};
page.onResourceRequested = function (request) {
if (requestIds.indexOf(request.id) === -1) {
requestIds.push(request.id);
requestCount++;
}
};
function checkLoaded() {
return page.evaluate(function () {
return document.all["compositionComplete"];
}) != null;
}
// Open the page
page.open(system.args[1], function () {
});
var checkComplete = function () {
// We don't allow it to take longer than 5 seconds but
// don't return until all requests are finished
if ((new Date().getTime() - lastReceived > 300 && requestCount === responseCount) || new Date().getTime() - startTime > 10000 || checkLoaded()) {
clearInterval(checkCompleteInterval);
console.log(page.content);
phantom.exit();
}
}
// Let us check to see if the page is finished rendering
var checkCompleteInterval = setInterval(checkComplete, 300);
8.Layout.cshtml Based On:
<!DOCTYPE html>
<html ng-app="appOne">
<head>
<meta name="fragment" content="!">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="utf-8" />
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
<base href="/">
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
<script src="~/Scripts/angular/angular.js"></script>
<script src="~/Scripts/angular/angular-route.js"></script>
<script src="~/Scripts/angular/angular-animate.js"></script>
<script>
angular.module('appOne', ['ngRoute'], function ($routeProvider, $locationProvider) {
$routeProvider.when('/one', {
template: "<div>one</div>", controller: function ($scope) {
}
})
.when('/two', {
template: "<div>two</div>", controller: function ($scope) {
}
}).when('/', {
template: "<div>home</div>", controller: function ($scope) {
}
});
$locationProvider.html5Mode({
enabled: true
});
});
</script>
</head>
<body>
<div id="body">
<section ng-view></section>
#RenderBody()
</div>
<div id="footer">
<ul class='xoxo blogroll'>
<li>one</li>
<li>two</li>
</ul>
</div>
</body>
</html>
NOTE: PhantomJS cannot process Persian Links(UTF-8)
Third part, other things you may required to know...
I don't see any other thing that may be involved, if you saw one ask me i'll edit my question.
I wrote a small article on it, just some simple settings, it is tested and working see this
Add in your routing:
$locationProvider.html5Mode(true);
Index Page:
<base href="/">
And web.config:
<system.webServer>
...........
</system.webServer>
You are done.
Here's what i did:
for configuration:
<rule name="Crawler" stopProcessing="false">
<match url=".*"/> <!-- rule back-reference is captured here -->
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_URI}" matchType="Pattern" pattern="(.*)_escaped_fragment_=(.*)" ignoreCase="true" negate="true" /><!-- condition back-reference is captured here -->
</conditions>
<action type="Rewrite" url="{R:0}" /><!-- rewrite action uses back-references to condition and to rule when rewriting the url -->
</rule>
<rule name="Index Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/api/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Token" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/bundles/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Content/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Scripts/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/SiteMap/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/CallBackBank/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Error/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/HtmlSnapshot[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Flight[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Hotel[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Tour[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/TravelAgency[^s]?/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/Users[^s]?/" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
And here's for Filter:
var request = filterContext.RequestContext.HttpContext.Request;
if (request.Url == null)
return;
var url = request.Url.Scheme + "://" + request.Url.Authority + request.RawUrl; //THIS LINE (`RowUrl` contains rest of the path)
Related
GET works fine.
But in Chrome with POST method I have:
Failed to load resource: the server responded with a status of 405 (Method Not Allowed).
Failed to load http://localhost:49834/api/Rest/: Response for preflight has invalid HTTP status code 405.
In InterntExplorer I have: SCRIPT7002: XMLHttpRequest: Network error 0x80070005, Access denied.
Controller:
[EnableCors("*", "*", "*")]
public class RestController : ApiController
{
// POST: api/Rest
[HttpPost]
public void Post([FromBody]Request Request)
{
if (ModelState.IsValid)
{
db.Requests.Add(Request);
db.SaveChanges();
}
}
}
WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
Web.config:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="*" />
<add name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" />
</customHeaders>
</httpProtocol>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule"/> <!-- add this -->
</modules>
<handlers>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="WebDAV" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
AJAX POST:
var url = "http://localhost:49834/";
$.ajax({
type: "POST",
url: url + "api/Rest/",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify(submitData),
success: function (data) {
},
error: function (err) {
console.log(err);
alert(err.status + " - " + err.statusText);
}
})
I use IIS Express: applicationHost.config file in %userprofile%\documents\IISExpress\config folder => ExtensionlessUrl-Integrated-4.0 handler is configured like this verb="GET,HEAD,POST,DEBUG,PUT,DELETE"
GET works fine:
$.getJSON(url + "api/Rest").done(function (data) {
var mass = new Array();
$(data).each(function (index, e) {
ReqArray.push(e);
})
vm.requests(ReqArray);
});
There was some problem with my ajax, like this it works good:
var url = "http://localhost:49834/";
$.ajax({
type: "POST",
url: url + "api/Rest",
dataType: "json",
data: submitData,
success: function (data) {
},
error: function (err) {
console.log(err);
alert(err.status + " - " + err.statusText);
}
})
I need to compress json response, I have tried IIS dynamic compression and used below code to compress but compression is not working it always Content-Type:application/json; charset=utf-8, Gzip headers are properly send to server. I have tried the code below.There is nothing in failed request tracing. Please suggest a solution.
class CompressFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpRequestBase request = filterContext.HttpContext.Request;
string acceptEncoding = request.Headers["Accept-Encoding"];
if (string.IsNullOrEmpty(acceptEncoding)) return;
acceptEncoding = acceptEncoding.ToUpperInvariant();
HttpResponseBase response = filterContext.HttpContext.Response;
if (acceptEncoding.Contains("GZIP"))
{
response.AppendHeader("Content-encoding", "gzip");
response.Filter = new GZipStream(response.Filter, CompressionMode.Compress,true);
}
else if (acceptEncoding.Contains("DEFLATE"))
{
response.AppendHeader("Content-encoding", "deflate");
response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
}
}
}
I have this settings in web.config file
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" staticCompressionLevel="9" />
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/javascript" enabled="true" />
<add mimeType="application/javascript; charset=utf-8" enabled="true" />
<add mimeType="application/json" enabled="true" />
<add mimeType="application/json; charset=utf-8" enabled="true" />
<add mimeType="*/*" enabled="false" />
</dynamicTypes>
<staticTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/atom+xml" enabled="true" />
<add mimeType="application/xaml+xml" enabled="true" />
<add mimeType="*/*" enabled="false" />
</staticTypes>
</httpCompression>
I am creating an update to my companies system that will be running on several clients and I have two config files, the old config file and the newer version.
Is there any way that I can compare the both files and check the differences to add to the older file what I have new in the first one?
Keep in mind that the files may have different info and the only thing that I need to add/change is the keys. For example, if a key is different change the older to that new "version", if a key doesn’t exist in the older files add it.
they keys will have the exact same name but may have different values. Plus there could be a new key that doesnt exist in the older file and I need to add it
I will leave an example of the files for you to see,
Any help would be appreciated.
<configuration>
<appSettings>
<add key="ORCASRV1" value="ORCA30|tcp://127.0.0.1:9001" />
<add key="ORCASRV2" value="REORCA30|tcp://127.0.0.1:9001" />
<add key="ServidorEmail" value="xxx" />
<add key="SqlTrans" value="1" />
<add key="RemoteType" value="0" />
<add key="LocalPort" value="9002" />
<add key="LocalMsgStore" value="1" />
<add key="sqlCHAR_TO_DATA" value="CONVERT(datetime, '#MM#/#DD#/#YYYY#')" />
<add key="sqlDATA_TO_CHAR" value="CONVERT(char(30), #CAMPO#)" />
<add key="sqlDATAPARTE" value="LTRIM(STR(DATEPART(#PARTE, #CAMPO#)))" />
<add key="sqlNUM_TO_CHAR" value="LTRIM(STR(#VALOR#))" />
<add key="sqlSYSDATE" value=" GetDate() " />
<add key="sqlALIAS" value=" As " />
<add key="sqlCONCATENAR" value="+" /> <add key="sqlNULL" value="IsNull(#CAMPO#,#VALOR#)" />
<add key="sqlROUND" value="ROUND(#CAMPO#,#PARTE#)" />
<add key="sqlLPAD" value="RIGTH(REPLICATE('#CHAR#',#VEZES#)+#CAMPO#,#VEZES#)" />
<add key="oraCHAR_TO_DATA" value="TO_DATE('#MM#/#DD#/#YYYY#','MM/DD/YYYY')" />
<add key="oraDATA_TO_CHAR" value="TO_CHAR(#CAMPO#, 'DD/MM/YYYY')" />
<add key="oraDATAPARTE" value="TO_CHAR(#PARTE#, #CAMPO#)" />
<add key="oraNUM_TO_CHAR" value="TO_CHAR(#VALOR#)" />
<add key="oraSYSDATE" value=" SYSDATE " />
<add key="oraALIAS" value=" " />
<add key="oraCONCATENAR" value="||" />
<add key="oraNULL" value="NVL(#CAMPO#,#VALOR#)" />
<add key="oraROUND" value="ROUND(#CAMPO#,#PARTE#)" />
<add key="oraLPAD" value="LPAD(#CAMPO#,#VEZES#,#CHAR#)" />
<add key="EmailCDP" value="antonio.santos#cdp-si.pt" />
<add key="EmailCliente" value="xxx" />
<add key="RPT_PATH1" value="C:\PROD\ORCAREPORT\" />
<add key="StartPage_Height" value="90" />
<add key="StartPage_Margem" value="220" />
<add key="StartPage_Espaco" value="5" />
<add key="StartPage_Intervalo" value="2" />
<add key="StartPage_Mais" value="35" />
<add key="HelpExec" value="WINHLP32.EXE" />
<add key="HelpFile" value="ORCA.HLP" />
<add key="LogLevel" value="0" />
<add key="LogSqlClient" value="0" />
<add key="LogFile" value="C:\cdpsi\logs" />
</appSettings>
<system.runtime.remoting>
<application>
<channels>
<channel ref="tcp" port="9002">
<clientProviders>
<formatter ref="binary" /> <provider type="CdpCompress.CompressionClientSinkProvider, CdpCompress" />
</clientProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Code went like this and it works like a charm:
public void UpdateService(string FilePathOld, string FilePathNew, string LatestVersion)
{
Dictionary<string, string> Old = new Dictionary<string, string>();
Dictionary<string, string> New = new Dictionary<string, string>();
if (ExisteFicheiro(FilePathNew) == true && ExisteFicheiro(FilePathOld) == true)
{
ExeConfigurationFileMap configOld = new ExeConfigurationFileMap();
configOld.ExeConfigFilename = FilePathOld;
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configOld, ConfigurationUserLevel.None);
ExeConfigurationFileMap configNew = new ExeConfigurationFileMap();
configNew.ExeConfigFilename = FilePathNew;
Configuration config2 = ConfigurationManager.OpenMappedExeConfiguration(configNew, ConfigurationUserLevel.None);
KeyValueConfigurationCollection settings = config.AppSettings.Settings;
Old = settings.AllKeys.ToDictionary(key => key, key => settings[key].Value);
KeyValueConfigurationCollection settings2 = config2.AppSettings.Settings;
New = settings2.AllKeys.ToDictionary(key => key, key => settings2[key].Value);
foreach (var NewKey in New)
{
string value;
if (Old.TryGetValue(NewKey.Key, out value))
{
if (value != NewKey.Value)
{
//if (ExistsKey(NewKey.Key, false) == true)
Old[NewKey.Key] = NewKey.Value;
}
}
else
{
Old.Add(NewKey.Key, NewKey.Value);
}
}
foreach (var NewKey in Old)
{
string key = NewKey.Key;
string value = NewKey.Value;
if (config.AppSettings.Settings[key] != null)
{
config.AppSettings.Settings[key].Value = value;
if (key == "Version")
config.AppSettings.Settings[key].Value = LatestVersion;
}
else
{
config.AppSettings.Settings.Add(key, value);
}
if (config.AppSettings.Settings["Version"] == null)
{
config.AppSettings.Settings.Add("Version", LatestVersion);
}
}
config.Save();
}
else
{
Erro NovoErro = new Erro();
Global.Erro = "O ficheiro \"OrcaService.exe.config\" ou o ficheiro \"Orca.exe.config\" não existem nos caminhos especificados!";
}
}
I have an asp.net MVC 5 project and I'm trying to throw a 404 error instead of 500.
The errors are
A public action method 'something' was not found on controller
'ProjetX.Controllers.HomeController'
and
The controller for path '/something' was not found or does not implement
IController
I understand why it's an error 500 but I would like to throw a 404. It would be better for SEO.
I can't figure out how to
Here's my code
My ExceptionHandler class for elmah
public class HandleCustomError : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
//If the exeption is already handled we do nothing
if (filterContext.ExceptionHandled)
{
return;
}
else
{
//Log the exception with Elmah
Log(filterContext);
Type exceptionType = filterContext.Exception.GetType();
//If the exception is while an ajax call
if (exceptionType == typeof(ExceptionForAjax))
{
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.ContentEncoding = Encoding.UTF8;
filterContext.HttpContext.Response.HeaderEncoding = Encoding.UTF8;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.HttpContext.Response.StatusDescription = filterContext.Exception.Message;
}
else
{
base.OnException(filterContext);
}
}
//Make sure that we mark the exception as handled
filterContext.ExceptionHandled = true;
}
private void Log(ExceptionContext context)
{
// Retrieve the current HttpContext instance for this request.
HttpContext httpContext = context.HttpContext.ApplicationInstance.Context;
if (httpContext == null)
{
return;
}
// Wrap the exception in an HttpUnhandledException so that ELMAH can capture the original error page.
Exception exceptionToRaise = new HttpUnhandledException(message: null, innerException: context.Exception);
// Send the exception to ELMAH (for logging, mailing, filtering, etc.).
ErrorSignal signal = ErrorSignal.FromContext(httpContext);
signal.Raise(exceptionToRaise, httpContext);
}
}
How I add the custom errors
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleCustomError());
}
The routes config
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Robots.txt",
"robots.txt",
new { controller = "robot", action = "index" });
routes.MapRoute(
name: "Localization",
url: "{lang}/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { lang = #"^[a-zA-Z]{2}$" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"NotFound",
"{*url}",
new { controller = "Error", action = "Error404" }
);
routes.MapMvcAttributeRoutes();
}
My webconfig
<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<!-- 2MB-->
<httpRuntime targetFramework="4.5" maxRequestLength="2097152" />
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
</httpModules>
<!-- Set mode to RemoteOnly in production -->
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="/Error/Error500">
<error statusCode="400" redirect="/Error/Error400" />
<error statusCode="404" redirect="/Error/Error404" />
<error statusCode="500" redirect="/Error/Error500" />
</customErrors>
</system.web>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="400" subStatusCode="-1" />
<error statusCode="400" path="/Error/Error400" responseMode="ExecuteURL" />
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/Error/Error404" responseMode="ExecuteURL" />
<remove statusCode="500" subStatusCode="-1" />
<error statusCode="500" path="/Error/Error500" responseMode="ExecuteURL" />
</httpErrors>
<modules>
<remove name="FormsAuthenticationModule" />
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
</modules>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="Robots-ISAPI-Integrated-4.0" path="/robots.txt" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<staticContent>
</staticContent>
</system.webServer>
I wanted to handle the error in the HandleCustomError class but the problem is that it goes straight to my Error500 action in the error controller.
What's weird is that the error is still logged in elmah.
It doesn't hit any breakpoint inside the HandleCustomError class, how can the error be logged?
Thank you
Here's the final code that works
I had to add 2 functions to generate a regex with all the name of my controllers and actions
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Localization",
url: "{lang}/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { lang = #"^[a-zA-Z]{2}$", controller = GetAllControllersAsRegex(), action = GetAllActionsAsRegex }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = GetAllControllersAsRegex(), action = GetAllActionsAsRegex() }
);
routes.MapRoute(
"NotFound",
"{*url}",
new { controller = "Error", action = "Error404" }
);
routes.MapMvcAttributeRoutes();
}
private static string GetAllControllersAsRegex()
{
var controllers = typeof(MvcApplication).Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(Controller)));
var controllerNames = controllers
.Select(c => c.Name.Replace("Controller", ""));
return string.Format("({0})", string.Join("|", controllerNames));
}
private static string GetAllActionsAsRegex()
{
Assembly asm = Assembly.GetExecutingAssembly();
var actions = asm.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type)) //filter controllers
.SelectMany(type => type.GetMethods())
.Where(method => method.IsPublic && !method.IsDefined(typeof(NonActionAttribute)))
.Select(x=>x.Name);
return string.Format("({0})", string.Join("|", actions));
}
}
See Also
https://stackoverflow.com/a/4668252
I have just change framework of my website from 2.0 to 4.0. And I am getting error HTTP Error 404.0 - Not Found whenever I try to open URL Rewriting webpage.
It was working fine at a time of Framework 2.0, don't know what is the wrong now. I have google many things but din't get any proper solution.
Please help me to solve the problem.
My Code is as below :
http://www.theprojectjugaad.com/PlacementAssistance.html -> Not Working
http://www.theprojectjugaad.com/PlacementAssistance.aspx -> Working
Global.asax :
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
void Application_BeginRequest(object sender, EventArgs ex)
{
if (!Request.Url.Host.Equals("localhost")
&& !Request.Url.Host.ToString().Contains("www.theprojectjugaad.com")
&& Request.Url.Host.ToString().Contains("theprojectjugaad.com"))
{
string Result = string.Concat(
"http://",
Request.Url.Authority.Replace("theprojectjugaad.com", "www.theprojectjugaad.com"),
HttpContext.Current.Response.ApplyAppPathModifier(Request.Path),
Request.Url.Query);
HttpContext.Current.Response.Redirect(Result, true);
}
}
private static void RegisterRoutes()
{
System.Web.Routing.RouteTable.Routes.Add(
"PlacementAssistance", new System.Web.Routing.Route("PlacementAssistance.html",
new RouteHandler("~/PlacementAssistance.aspx")));
}
RouteHandler.cs :
public RouteHandler(string virtualPath)
{
_virtualPath = virtualPath;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var display = BuildManager.CreateInstanceFromVirtualPath(
_virtualPath, typeof(Page)) as IDisplay;
return display;
}
string _virtualPath;
IDisplay.cs :
public interface IDisplay : IHttpHandler
{
}
web.config :
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="Session" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" preCondition="" />
</modules>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="UrlRoutingHandler" />
</handlers>
<rewrite>
<rules>
<remove name="Plesk. SEO-safe redirect for http://www.theprojectjugaad.com" />
<rule name="Plesk. SEO-safe redirect for http://www.theprojectjugaad.com" enabled="false" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{HTTP_HOST}" pattern="www.theprojectjugaad.com" />
<add input="{HTTPS}" pattern="OFF" />
</conditions>
<serverVariables />
<action type="Redirect" url="http://theprojectjugaad.com/{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
What is the URL that is not working?
What I see is you've corrected the URL from .html to .aspx and it works.