I am writing a handler to process a resource and I am facing an issue, when the implemented IHttpHandler Class is written under a namespace.
Please find the code
Not working scenario
Web.config:
<add name="ResourceHandler" type="PublicSite.Classes.Handlers.ResourceEndpoint, PublicSite" path="Resource.ashx" verb="*" />
Property: PublicSite.Classes.Handlers.ResourceEndpoint.IsReusable=False
Issue: Constructor is hit and the ProcessRequest is not invoked. Constructs the object of the class, ie., Constructor is hit , however fails by 404 before the processrequest is invoked.
Working Scenario
Web.config:
<add name="ResourceHandler" type="ResourceEndpoint,PublicSite" path="Resource.ashx" verb="*" />
Class: PublicSite.Classes.Handlers.ResourceEndpoint.IsReusable=True
Constructor is hit and fails by 404 before the processrequest is invoked.
Second time when we request for the handler, it straight away uses the constructed object so construcotr is not invoked and the ProcessRequest is invoked directly. By the way in the second call the handler is working.
I dont require the reusable property.
Can some one help me to find the issue. Thanks.
Can you confirm to me that your web.config and handler itself looks like the following ? (This is from a project hosted under IIS 7.5)
web.config: -
<system.webServer>
<handlers>
<add name="customerHandler"
preCondition="integratedMode"
verb="*"
path="customerHandler.ashx"
type="GlassCMS.HttpHandlers.CustomerHandler, GlassCMS"/>
.
.
.
</handlers>
</system.webServer>
HttpHandler: -
[WebService(Namespace = "http://{redacted.com}/json-http-handlers/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CustomerHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
string json = string.Empty;
// code to do whatever here...
context.Response.Write(json);
}
I can include the JavaScript as well if you want (or you can post yours here). This example above is from working code.
Related
I am trying to setup a .net core 2.2 web api to use a post verb. Anything other than a get verb returns a 405 no matter if it is run on my local machine (w10 iis eXPRESS 10.0) or the windows server (2016 R2 IIS 8.0). I've read the other posts about disabling WebDav in your config file, adding a route, and completely removing the WebDav feature. I have done all of those to no avail. I'm just starting to develop in core and find this baffling, on the same server is a non-core web api that runs on .net framework 4.5 that processes GET,PUT,POST,DELETE without error. And yes, I have restarted the server after making changes to any of the configurations. The following are the web.config changes that I made, the last one coming directly from MS. Basic project that reproduces the same error on my machine and server is here https://github.com/FranciscanMedia/error405_core/tree/master it is just a standard web api project you get when you fire up VS2019.
<system.webServer>
<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" />
</handlers>
</system.webServer>
<system.webServer>
<modules>
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
</handlers>
</system.webServer>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="false">
<remove name="WebDAVModule"/>
</modules>
</system.webServer>
<system.webServer>
<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" />
</handlers>
</system.webServer>
Short answer
It could be as simple as that. The reason is routing.
Just send your POST request to right URL like https://localhost:44327/api/values/123.
Detailed explanation
It's not the issue. It works as expected.
You make a GET request to https://localhost:44327/api/values/. It returns 200 OK.
But when you make a POST request to the same URL https://localhost:44327/api/values/. It says 405 Method not allowed.
However, you get 405. It is happening because you are hitting the GET endpoint with POST method.
Microsoft Docs says:
... the HTTP client sent a valid JSON request to the URL for a Web API application on a web server, but the server returned an HTTP 405 error message which indicates that the PUT method was not allowed for the URL. In contrast, if the request URI did not match a route for the Web API application, the server would return an HTTP 404 Not Found error.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications
If you simply remove the GET endpoint. The POST request will start returning 404 Not found. Which means that you are not hitting any registered route.
To send POST request you need to use different URL according to the routing rules.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// POST api/values
[HttpPost("{val}")]
public StatusCodeResult Post()
{
return Ok();
}
}
This attribute-based configuration means that route of your POST endpoint is /api/Values/{val}. Where {val} is any value. It's not processed in the endpoint.
If you want to process it, you should pass it to the method:
[HttpPost("{val}")]
public StatusCodeResult Post(string val)
{
return Ok();
}
I think that in your controller you have to import another library.
Try
using System.Web.Http;
Instead of
using Microsoft.AspNetCore.Mvc
Looking at what you have defined:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
Then for the action:
[HttpPost("{val}")]
public StatusCodeResult Post()
{
return Ok();
}
Your routing matches the following url:
https://localhost:44327/api/values/StatusCodeResult
It is going to take your main route defined on your controller [Route("api/[controller]")]
Then you are defining the "template" to use "{val}"
This is telling it to use the ActionResult specific name and to expect var val to be passed/appened.
Checking out the official documentation here: https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.2
under section "Token replacement in route templates ([controller], [action], [area])"
They specifiy:
For convenience, attribute routes support token replacement by enclosing a token in square-braces ([, ]). The tokens [action], [area], and [controller] are replaced with the values of the action name, area name, and controller name from the action where the route is defined. In the following example, the actions match URL paths as described in the comments:
[Route("[controller]/[action]")]
public class ProductsController : Controller
{
[HttpGet] // Matches '/Products/List'
public IActionResult List() {
// ...
}
[HttpGet("{id}")] // Matches '/Products/Edit/{id}'
public IActionResult Edit(int id) {
// ...
}
}
If you want it to just route based on just verbs (follow a pattern where each api endpoint just handles operations for that specific object) then you would change your post method to just
[HttpPost]
public ActionResult Post(string val)
{
return Ok();
}
I totally agree with #Vladimir's answer. I dont have enough points to add comments to the answer by #vlaimir so i am adding my thoughts and suggestions.
The code you have on your github,
// POST api/values
[HttpPost("{val}")]
public StatusCodeResult Post()
{
return Ok();
}
This is a post and it would expect a value {val} per the route action configuration. Since you may try to hit the post without any value, thats not permitted. Ensure you supply some value and then do the POST. If you are using POSTMAN, you may have to supply the BODY of your request with some value. Swagger is a great util tool to embed into the web api's and that comes with excellent intuitive UI for our routes/resources. That might be even ideal to help determine and ensure you supply the right value.
Otherwise, you dont need to modify or worry about your IIS or IIS Express settings. or webdav.
I have an ASP.NET application with a filter wired up in RegisterGlobalFilters that performs the following:
public class XFrameOptionsAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(System.Web.Mvc.ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.AddHeader("X-FRAME-OPTIONS", "SAMEORIGIN");
}
}
Looking in Fiddler, I can see that views returned from the webserver include this header. Static files however, such as JavaScript do not include this header in the HTTP response.
How do I get ASP.NET MVC to also apply this filter to any static files the web server returns?
One way to set headers for all the content of site is in web.config. The customHeaders section will make sure that this header is included for all files and responses.
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="X-FRAME-OPTIONS" value="SAMEORIGIN" />
</customHeaders>
</httpProtocol>
</system.webServer>
Another option is to create custom HttpModule as shown below. This way you have more control on the files and content to which headers needs to be appended.
namespace MvcApplication1.Modules
{
public class CustomOriginHeader : IHttpModule
{
public void Init(HttpApplication context)
{
context.PreSendRequestHeaders += OnPreSendRequestHeaders;
}
public void Dispose() { }
void OnPreSendRequestHeaders(object sender, EventArgs e)
{
// For example - To add header only for JS files
if (HttpContext.Current.Request.Url.ToString().Contains(".js"))
{
HttpContext.Current.Response.Headers.Add("X-FRAME-OPTIONS", "SAMEORIGIN");
}
}
}
}
And then register them in web.config as shown below -
<system.webServer>
<modules>
<add name="CustomHeaderModule" type="MvcApplication1.Modules.CustomOriginHeader" />
</modules>
</system.webServer>
This is something that if you want on every request (static or dynamic requests), you should probably set it up through IIS (the web server). Here are some details on different ways that you can achieve this - http://www.iis.net/configreference/system.webserver/httpprotocol/customheaders
In short, you could do this in your web.config file
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="X-Custom-Name" value="MyCustomValue" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
If you have access directly to IIS, you can use the UI to set this up as well.
Im running a website on WindowsServer 2008 R2, and IIS 6.1 sp1
I created an empty ASP.NET 4.0 web application, and added an http handler to it. The job of the handler will be to redirect incoming calls to other sites based on a tokenid passed in the querystring (from a federated single sign on provider).
To test the connection, I am just parsing the token into a Dictionary and writing the information to the context.Response.
The handler works on my machine, but when I deploy it, I get 500 and 403 errors.
I get 500 errors when I make the application pool ASP.NET 4.0 Integrated, I get 404 errors when make the application pool ASP.NET 4.0 Classic
If I add a test.htm to the directory, I can access the test.htm and see its contents, but I would be expecting to see the output from the handler, so it makes me think it cant find the handler.
Here is the web.config followed by the code
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpHandlers>
<add verb="*" path="*"
type="RedirectSite.RedirectHttpHandler, RedirectSite, Version=1.0.0.0, Culture=neutral" />
</httpHandlers>
</system.web>
<configuration>
public class RedirectHttpHandler : IHttpHandler
{
public RedirectHttpHandler()
{ }
public void ProcessRequest(HttpContext context)
{
// check for tokenid in querystring
string tokenid = context.Request.Params["tokenid"];
string agentid = context.Request.Params["agentid"];
Dictionary<string,string> tokenItems = TokenParser.Parse(tokenid, agentid);
context.Response.Clear();
context.Response.Write("<b>Token Information</b><br/><br/>");
foreach (KeyValuePair<string, string> item in tokenItems)
{
context.Response.Write(String.Format("{0} : {1}<br/>",item.Key,item.Value));
}
}
public bool IsReusable { get; private set; }
}
Turns out that the ISAPI/CGI settings for the site were preventing 4.0 - once enabled, the site worked
I have an application done in .netframework 2.0 and trying to use an authentication handler in a security project, which is written in 3.5 framework. Also I m using IIS 7
Web.config of the application has the following entry
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<handlers accessPolicy="Read, Write, Script, Execute">
<add name="Pdfhandler" verb="*" path="/calderdale/*.pdf" type="NES.HiLo.Security.CalderDaleAuthenticationHandler, NES.HiLo.Security" preCondition="integratedMode" />
</handlers>
</system.webServer>
The code for CalderDaleAuthenticationHandler is
using System;
using System.Web;
namespace NES.HiLo.Security
{
public class CalderDaleAuthenticationHandler : IHttpHandler
{
/// <summary>
/// You will need to configure this handler in the web.config file of your
/// web and register it with IIS before being able to use it. For more information
/// see the following link: http://go.microsoft.com/?linkid=8101007
/// </summary>
#region IHttpHandler Members
public bool IsReusable
{
// Return false in case your Managed Handler cannot be reused for another request.
// Usually this would be false in case you have some state information preserved per request.
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
//var application = (HttpApplication)sender;
//var context = application.Context;
HttpRequest request = context.Request;
HttpResponse response = context.Response;
// Check if the user is authenticated
}
#endregion
}
}
In my application I have a folder name calderdale and I have some pdf files. when I type in some thing like below to access the pdf file. I am expecting the control to go to handler, where I have set breakpoints. The control never goes to the handler. I appreciate any help.
http://local.knowledge.scot.nsh.uk/calderdale/abc.pdf
I used httphandlers to intercept the request. Then added a handler like this in web.config
<httpHandlers>
<add verb="GET" path="calderdale/*.pdf"
type="NES.HiLo.Security.CalderDaleAuthenticationHandler, NES.HiLo.Security" />
</httpHandlers>
After the above on IIS 7.0 I added the following handler from IIS Handlers section
<system.webServer>
<handlers>
<add name="Calderdale Handler" path="calderdale/*.pdf"
verb="GET" modules="IsapiModule"
scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll"
resourceType="Unspecified" requireAccess="Script"
preCondition="classicMode,runtimeVersionv2.0,bitness32" />
</handlers>
</system.webServer>
I am having quite a few problems with custom extensions and intercepting existing handlers.
What am I trying to do
Based upon persisted options, I would like all 'virtual' extensions to be handled by set handlers. All pages are dynamically built, and no actual files exist on the site. The site populates the content, forms the html output and returns it as the web result.
This is required as I am setting up a fat/thin relationship between 2 servers. The thin server will simply pass on the request to the fat server - where the request is processed and response issued back down the line.
The project is for a dynamic multi-domain content management system. The thin server may not be .net compatible (hence the external request), but will be .net optimised (hence the need for handlers).
The Problem
What I want is to re-route existing extensions - aspx; php; html.
I have achieved this in my local environment using a custom HttpModule which sets the appropriate handler. I have explored setting the tag in config, but the the extensions are re-routed using dynamic rules that are persisted.
As mentioned, this solution works on localhost.
When uploaded, the .Net extensions are handled by the module correctly but any custom extensions or non-.net extensions return a 404 error.
Seeking an alternative, I have experimented with routing within Global, but this dis not work either.
I have also attempted to use to register the custom extensions... but each are met with the same result - 404 not found.
Global Routing attempt:
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new Route("{action}.sqs", new SqlRequestHandler()));
}
.Config (for handler and module attempt)
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpRuntime requestValidationMode="2.0" />
<customErrors mode="Off"/>
<httpHandlers>
<add path="*.sqs" verb="*" type="CmsMapper.VirtualHandler, CmsMapper" />
<add path="*.sql" verb="*" type="CmsMapper.VirtualHandler, CmsMapper" />
</httpHandlers>
<httpModules>
<add name="SisBerCMS" type="CmsMapper.VirtualModule, CmsMapper" />
</httpModules>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<modules>
<add name="SisBerCMS" type="CmsMapper.VirtualModule, CmsMapper" />
</modules>
<handlers>
<add path="*.sqs" verb="*" type="CmsMapper.VirtualHandler, CmsMapper" name="sqsHandler" />
<add path="*.sql" verb="*" type="CmsMapper.VirtualHandler, CmsMapper" name="sqlHandler" />
</handlers>
</system.webServer>
Custom Module (CmsMapper.VirtualModule)
if (extentionMap != null)
{
// note that extentionMap.ExtentionType is a predetermined enum
switch (extentionMap.ExtentionType)
{
// If the extention is banned then pass back a generic message
case ExtentionType.Banned:
this.WriteTextResponce("Invalid extention detected:" + extentionMap.Extention);
break;
// Remap .Ajax requests to the ajax handler
case ExtentionType.Ajax:
this._app.Context.RemapHandler(new AjaxHandler());
break;
// Remap session query or sql requests to the sql handler
case ExtentionType.SessionQuery:
this._app.Context.RemapHandler(new SqlRequestHandler());
break;
// if the extention is not ignored, re map to the virtual page handler
default:
bool isManagementServer = this._app.Context.Request.Url.Authority != VirtualModule.RESPONSE_SERVER;
bool isPostRequest = !String.IsNullOrEmpty(this._app.Context.Request.Form[HtmlRequest.RequestOrigin]);
bool isGetRequest = !String.IsNullOrEmpty(this._app.Context.Request.QueryString[HtmlRequest.RequestOrigin]);
bool isIgnored = extentionMap.ExtentionType == ExtentionType.Ignore;
if ((isPostRequest || isGetRequest) && !isIgnored)
{
this._app.Context.RemapHandler(new VirtualHandler());
}
else
{
this._app.Context.RemapHandler(new ExternalRequestHandler());
}
break;
}
}
All the handlers are pretty standard implementing the following:
public class SqlRequestHandler : IHttpHandler, IRequiresSessionState, IRouteHandler
Again, the preferred method - HttpModule - works on my localhost machine. This could be a server config issue (in which case I'm looking for a work around), but the fact that the .net extensions are being handled is strange - as this would imply that issues with medium trust should not apply, however issues regarding extension handling on the server may take priority over the .net application.
The server is shared hosting (therefore I am unable to alter the machine.config files), is IIS6 using 4.0.
Thank you for any suggestions on how to resolve this issue.
Mike
You need to configure web site in IIS 6.0 to route all extensions (including extensionless paths known as wildcard extension mapping) to ASP.NET ISAPI dll (and disable the check for file exists).
You can of course do this mapping selectively only for those extensions that you want to route via ASP.NET code. But wildcard mapping will be more useful in case you don't have predefined set of extensions.
In the absence of such mappings, IIS will not forward requests for unknown extensions to ASP.NET (and routing code will not even come into picture) - rather IIS will pass the extension to default (static file) handler that will issue 404 if file is not present.
See this article that describes these steps (for ASP.NET MVC but the same applies to your case): http://haacked.com/archive/2008/11/26/asp.net-mvc-on-iis-6-walkthrough.aspx
Near the end of article, author has given how to add wildcard script map