How can I accomplish URL rewriting in ASP.NET Core RC2? Migrating from RC1 to RC2 broke my Angular 2 routing when I refresh the page.
I was using a rule like this before in my web.config located in my wwwroot. With RC2 I'm not even sure if I'm supposed to have a web.config in my wwwroot anymore, or if I'm just supposed to have the one in the base of my project.
This is my base web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" forwardWindowsAuthToken="true" stdoutLogEnabled="true" />
</system.webServer>
</configuration>
And this is my wwwroot web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<!--Redirect selected traffic to index -->
<rule name="Index Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/api/" negate="true" />
<add input="{REQUEST_URI}" matchType="Pattern" pattern="^/account/" negate="true" />
</conditions>
<action type="Rewrite" url="/index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
When I refresh an angular 2 route, I get a Status Code: 404; Not Found from ASP.NET
I found a working solution here.
Below is the code in my Configure method that fixed the issue for me.
But be careful, the declaration order matters.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.Use(async (context, next) => {
await next();
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value)) {
context.Request.Path = "/";
context.Response.StatusCode = 200;
await next();
}
});
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc(routes => {
routes.MapRoute("Default", "api/{controller}/{action}/{id?}");
});
}
I found solution by Steve Sanderson, seems to work.
he wrote extension to RouteBuilder.
You can configure it in Setup.cs by calling extension method MapSpaFallbackRoute
https://github.com/aspnet/JavaScriptServices
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
});
I had the exact same issue. In fact, for me, the IIS Express Server did not even start when the rewrite rule was included in the web.config file. Given this I dug around for some other way to do the same thing without relying on rewrite rules. I found that you can use the MapWhen function in your startup.cs file to send anything not handled by MVC back to index.html.
The below code has been added to the Configure method of the Startup.cs class after the app.UseMvc() call.
app.UseMvc();
// this serves "index.html" from the wwwroot folder when
// a route not containing a file extension is not handled by MVC.
app.MapWhen(context =>
{
var path = context.Request.Path.Value.ToLower();
return path.Contains(".");
},
branch =>
{
branch.Use((context, next) =>
{
context.Request.Path = new PathString("/index.html");
return next();
});
branch.UseStaticFiles();
});
So far this looks like its working but I need to do some more testing to see if there are any side effects. Personally the rewrite rule seemed a better solution but this does appear to get me past the issue.
Related
Apologies for repeat but i've tried every suggestion to every question even vaguely similar to this to no end.
ajax post:
var contactForm = $('#contactForm');
contactForm.on('submit', (event) => {
event.preventDefault()
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN' : $('meta[name="csrf-token"]').attr('content')
}
})
console.log(contactForm.serializeArray())
$.ajax({
dataType: "json",
method : 'POST',
url : 'contact-us/post',
data : contactForm.serializeArray(),
success: (res) => {
console.log(res)
}
})
})
Route handling post:
Route::post('contact-us/post', 'EnquiryPostController#store');
PostController store method:
public function store(Request $request)
{
return response()->json(['success'=>$request->all()]);
}
Console output
response header from network tab
request header from network tab
from data from network tab
UPDATE:
Just to clarify:
I am passing the CSRF token.
The form inputs have name attributes.
The PostController is receiving the POST request but simply does not contain any form data.
This issue happens with AJAX POST and regular old form POST
Turns out this was not a Laravel issue but a Windows Server/web.config issue. I'd setup my site using the XML found here. After much trial and error the issue was solved using this XML:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<ModSecurity enabled="true" configFile="c:\inetpub\wwwroot\modsecurity.conf" />
<httpErrors errorMode="Detailed" />
<modules runAllManagedModulesForAllRequests="true" />
<defaultDocument>
<files>
<clear />
<add value="index.php" />
</files>
</defaultDocument>
<rewrite>
<rules>
<rule name="CanonicalHostNameRule1">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^dev\.financialservicesexpo\.co\.uk$" negate="true" />
</conditions>
<action type="Redirect" url="http://dev.financialservicesexpo.co.uk/{R:1}" />
</rule>
<rule name="default" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/index.php" />
</rule>
</rules>
</rewrite>
<staticContent>
<clientCache cacheControlMaxAge="8.00:00:00" cacheControlMode="UseMaxAge"/>
</staticContent>
</system.webServer>
</configuration>
I wish I could explain why this fixed the issue but I have very little understanding of server configuration. If anyone would like to comment with insight I at least would find it interesting.
IF THIS DID NOT HELP PLEASE READ BELOW
Getting to the bottom of this issue involved trying a lot of different solutions found across even more questions posted to StackOverflow, etc. and was ultimately solved in two minutes after chatting to a colleague and working though the issue out loud. A frustrating lesson I keep needing to learn... So, in the hope this might help others the following is a list of the aforementioned solutions you should check.
Check your form inputs have name attributes.
Clear your browser cache.
Clear Laravel cache data.
Restart website
When using AJAX try both serialize() and serializeArray()
When using AJAX try submitting with different DataTypes and ContentTypes. The default should be fine but worth a shout if your doing something different.
CSRF token: Add it via either a hidden input field or set in the request header. Only one should be needed.
Make sure the /storage and /bootstrap folders have read/write privileges enabled.
If anyone has any suggestions or corrections to what I have written here please do speak up. I am no means a expert.
Try in your ajax request using type instead of method like
$.ajax({
dataType: "json",
type : 'POST',
url : 'contact-us/post',
data : contactForm.serializeArray(),
success: (res) => {
console.log(res)
}
})
You can use the below solution to send data:
data : contactForm.serialize()
I am trying to make an HTTP PUT with an integer parameter to a MVC WebApi.
I tried to follow the angular 2 guidelines for HTTP PUT: https://angular.io/docs/ts/latest/guide/server-communication.html
My WebApi:
public IHttpActionResult Put([FromBody]int id)
{
return Ok();
}
My Http PUT in my service in my ionic 2 app:
makePut(){
let body = JSON.stringify({id:155320});
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return new Promise(resolve => {
this.http.put('API_URL', body, options)
.subscribe(
response => {
console.log(response.text());
},
error => {
//Failed to Login.
alert(error.text());
console.log(error.text());
});
});
}
And finally the call to my service from my home page:
this.service.makePut().then(data => {console.log(data);});
When I run this I get a 405 method not allowed. Is there anything I am missing?
UPDATE here is part of the web.config in my web api:
<system.webServer>
<security>
</security>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule" />
</modules>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
<handlers>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<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>
It might be Web Api error and you can resolve that by adding this code to your web.config file
<handlers accessPolicy="Read, Script">
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<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" />
this article can give you more information about 405 error.
I was able to fix the problem, which turned out to be a CORS issue. You can view how to disable same origin policy in Chrome here: How to resolve CORS issue and JSONP issue in Ionic2
Once I disabled it in chrome, the HTTP PUT succeed.
Here is my final code:
makePut(){
let body = 155320; // this is different
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return new Promise(resolve => {
this.http.put('API_URL', body, options)
.subscribe(
response => {
console.log(response.text());
},
error => {
//Failed to Login.
alert(error.text());
console.log(error.text());
});
});
}
I have an ASP.NET WebForms app that is widely distributed. My new version adds Web.API and users have been playing with a beta. In most beta installations everything works fine, but for some installs all the Web.API calls return HTTP 404 Not Found errors. I can't figure out why the Web.API calls fail on some servers but works fine on others.
My guess is there is some kind of server configuration that is breaking the routing, but I can't figure out what it is. I've even RDP'd into one of these sites and can't find anything obvious.
This is an example API call:
GET http://site.com/api/events/27
The code:
namespace GalleryServerPro.Web.Api
{
public class EventsController : ApiController
{
public string Get(int id)
{
return "Event data for ID " + id;
}
}
}
The route definitions, which are called from the Init event of a custom HTTP module named GspHttpApplication:
private void RegisterRoutes(RouteCollection routes)
{
routes.MapHttpRoute(
name: "GalleryApi1",
routeTemplate: "api/{controller}"
);
routes.MapHttpRoute(
name: "GalleryApi2",
routeTemplate: "api/{controller}/{id}",
defaults: new { },
constraints: new
{
id = #"\d*",
}
);
routes.MapHttpRoute(
name: "GalleryApi3",
routeTemplate: "api/{controller}/{id}/{action}",
defaults: new
{
},
constraints: new
{
id = #"\d*"
}
);
// Add route to support things like api/meta/galleryitems/
routes.MapHttpRoute(
name: "GalleryApi4",
routeTemplate: "api/{controller}/{action}",
defaults: new
{
},
constraints: new
{
}
);
}
The example GET above should match the route named GalleryApi2, and like I said in most installations it does.
I know WebDAV can cause trouble (405 errors), so I already remove it in web.config:
<system.webServer>
<modules>
<remove name="WebDAVModule" />
<add name="GspApp" type="GalleryServerPro.Web.HttpModule.GspHttpApplication, GalleryServerPro.Web" />
</modules>
<handlers>
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<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>
So it all comes down to figuring out why this configuration works for some web installs and not others.
Roger
In asp.net mvc 3 I have a site which has an ssl certificate, and runs just fine in https. The only page exposed is the logon page. For whatever reason, it does not load in https. Here is my relevant code (will post more on request if I left something out).
web.config
<compilation debug="false" targetFramework="4.0">
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" requireSSL="true"/>
</authentication>
global.asax
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Account", action = "LogOn", id = UrlParameter.Optional }
);
}
account controller
#if !DEBUG
[RequireHttps]
#endif
public class AccountController : Controller
{
public ActionResult LogOn()
{
return View();
}
}
When the logon view loads, it is not in Https. What did I miss?
You need to set the Build Target to Release when you build your site. You will see a dropdown that looks like this in Visual Studio, change it to Release and rebuild, then publish your site:
You need to add [RequireHttps], so it would look like this:
[RequireHttps]
public ActionResult LogOn()
{
return View();
}
This will force it to use Https.
Edit
Perhaps you need to add, the following to your Web.Config's <system.webServer> section:
<rewrite>
<rules>
<rule name="Secure Account Controller" enabled="true" stopProcessing="true">
<match url="^account" ignoreCase="true" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="true">
<add input="{HTTPS}" pattern="off" />
<add input="{HTTP_HOST}" pattern="([^/:]*?):[^/]*?" />
</conditions>
<action type="Redirect" url="https://{C:1}:44300{URL}" />
</rule>
</rules>
</rewrite>
I've added Mini Profiler through NuGet and though in a really simple project works lovely, this is a big and existing project, and of course that I'm getting some problems with it :(
it writes the correct script tags in the source code as
<link rel="stylesheet" type="text/css" href="/mini-profiler-includes.css?v=1.9.0.0">
<script type="text/javascript">
if (!window.jQuery) document.write(unescape("%3Cscript src='/mini-profiler-jquery.1.6.2.js' type='text/javascript'%3E%3C/script%3E"));
if (!window.jQuery || !window.jQuery.tmpl) document.write(unescape("%3Cscript src='/mini-profiler-jquery.tmpl.beta1.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript" src="/mini-profiler-includes.js?v=1.9.0.0"></script>
<script type="text/javascript">
jQuery(function() {
MiniProfiler.init({
ids: ["e48fcf61-41b0-42e8-935a-fbb1965fc780","870a92db-89bc-4b28-a410-9064d6e578df","30881949-bfdb-4e3a-9ea5-6d4b73c28c1d","6bca31b8-69d9-48eb-b86e-032f4d75f646","df16838d-b569-47d0-93e6-259c03322394"],
path: '/',
version: '1.9.0.0',
renderPosition: 'left',
showTrivial: false,
showChildrenTime: false,
maxTracesToShow: 15
});
});
</script>
But when I try to open any file, I get a HTTP 404
I verified that there is a MiniProfiler.cs under App_Start and adding a break point there, the code runs, I even added
#region Mini Profiler
protected void Application_BeginRequest()
{
if (Request.IsLocal)
{
MiniProfiler.Start();
}
}
protected void Application_EndRequest()
{
MiniProfiler.Stop();
}
#endregion
to the global.asax file...
Is there something obviously that I'm missing?
This is a known issue with certain configurations of IIS.
The workaround is to ensure the UrlRoutingModule handles all the mini profiler includes in your web.config:
<system.webServer>
<handlers>
<add name="UrlRoutingModule1" path="mini-profiler*.js" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" />
<add name="UrlRoutingModule2" path="mini-profiler*.css" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" />
<add name="UrlRoutingModule3" path="mini-profiler*.tmpl" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" />
</handlers>
</system.webServer>
There are 2 open tickets on this issue at the moment:
http://code.google.com/p/mvc-mini-profiler/issues/detail?id=50
http://code.google.com/p/mvc-mini-profiler/issues/detail?id=60
In a future version, to avoid the issue, we will probably serve our includes extensionless.