Handling 405 errors - asp.net

I am trying to handle 405 (Method not Allowed) errors generated from WebApi.
E.g.: Basically this error will be handled whenever someone calls my Api with a Post request instead of a Get one.
I'd like to do this programatically (i.e. no IIS configuration), right now there are no documentation of handling this kind of error and the IExceptionHandler is not triggered when this exception happens.
Any ideas ?

Partial response:
By looking at the HTTP Message lifecycle from here, there is a possibility to add a Message Handler early in the pipeline, before HttpRoutingDispatcher.
So, create a handler class:
public class NotAllowedMessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
switch (response.StatusCode)
{
case HttpStatusCode.MethodNotAllowed:
{
return new HttpResponseMessage(HttpStatusCode.MethodNotAllowed)
{
Content = new StringContent("Custom Error Message")
};
}
}
}
return response;
}
}
In your WebApiConfig, in Register method add the following line:
config.MessageHandlers.Add(new NotAllowedMessageHandler());
You can check the status code of the response and generate a custom error message based on it.

Related

same HttpPut request code format not working but the works on another request

I have a HTTPPUT request that is not being called. I have a similar put request that manages another tab and it works. Both pages are pretty identical. I don't know what I am doing wrong.
I have tried almost everything and don't know what else to try.
controller:
[HttpPut]
[Route("updateAllocations({type})")]
public IHttpActionResult UpdateAllocations(string type, T_LOC entity)
{
System.Diagnostics.Debug.WriteLine("inside");
_allocationsService.UpdateAllocations(type,entity);
return Ok();
}
interface:
using OTPS.Core.Objects;
using System.Collections.Generic;
using OTPS.Core.Models;
namespace OTPS.Core.Interfaces
{
public interface IAllocationsService
{
void UpdateAllocations(string type, T_LOC entity);
}
}
service:
public void UpdateAllocations(string type, T_LOC entity)
{
System.Diagnostics.Debug.WriteLine("inside");
}
CLIENT SIDE:
public updateAllocation(type: string , entity) {
console.log("sdfsdf")
console.log(`${this.baseUrl}/api/allocations/updateAllocations(${type})`)
return this.http.put(`${this.baseUrl}/api/allocations/updateAllocations({type})`, entity, { headers: this.headers, withCredentials: true })
.pipe(catchError((error: Error) => {
console.log("sdfasd111111sdf")
return this.errorService.handleError(error);
}));
}
I am expecting the clinet side to call the put request before making any further logic but the print on server side never gets called..
Make sure that you subscribe to the service method inside component:
this.myService.updateAllocation(type, entity).subscribe( response => {
// do something here with response
});
You must call subscribe() or nothing happens. Just calling
Service method does not initiate the PUT/DELETE/POST/GET request.
Always subscribe!
An HttpClient method does not begin its HTTP request until you call
subscribe() on the observable returned by that method. This is true
for all HttpClient methods.

WebAPI controller throwing exception in background

I have a restful WebAPI application that handles requests from an AngularJS app. The controller actions requiring the GET verb works fine, but actions with POST/PUT that does not return any content will throw a Nullreference Exception.
Example:
[HttpPut]
public void Update()
This gives me the following message after function execution returns:
System.NullReferenceException: Object reference not set to an instance of an object. <SendAsync>d__0.MoveNext
It will also return HTTP status code 204 to the client.
By changing method signature to be async and return an empty object it will work as expected:
[HttpPut]
[Route("")]
public async Task<object> Update(ProfileViewModel model)
{
_profileManager.Update(model);
return Ok(new {});
}
Note that just returning status code 200 will not work. Some content must also be returned, or else the exception is thrown.
This happens on every request that is not GET, including DELETE. How can I fix this without having to change the signature and return an anonymous object for every single method?
Please try this code without any _profileManager.Update(model);
Use IActionResult if you are using .net core.
return Ok() does not force you to return anything and should work.
if that does not work then check for any errors in your custom serializer.
if that WORKS then put back your _profileManager.Update(model) and check if that is throwing any errors.
[HttpPut]
[Route("")]
public async Task<IHttpActionResult> Update(ProfileViewModel model)
{
return Ok();
}
You don't have to return object:
[HttpPost]
public IActionResult Update()
{
return Ok();
}

Web Api - how to stop the web pipeline directly from an OnActionExecuting Filter

I have a pre-action web api hook that will check ModelState.IsValid. If the ModelState is not valid I do not want to execute the action and just return my message immediately. How exactly do I do this?
public class ValidateModelStateAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) {
if (!actionContext.ModelState.IsValid)
{
var msg = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
// Now What?
}
base.OnActionExecuting(actionContext);
}
}
set the Response.Result. If the result is not null it will not execute the action. the exact syntax is escaping me right now, but it's as simple as
if(actionContext.ModelState.IsValid == false)
{
var response = actionContext.Request.CreateErrorResponse(...);
actionContext.Response = response;
}
Have you actually seen the example on the ASP.NET WebApi page?
Looks very much like what you're trying to achieve and all they do is setting the Response of the Context object:
If model validation fails, this filter returns an HTTP response that contains the validation errors. In that case, the controller action is not invoked.
http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api
see: Handling Validation Errors
My guess is that you should throw a HttpResponseException

Accessing current controller executing in DelegatingHandler

I was wondering if it's possible to access the controller being executed (or about to be executed) in the SendAsync method of the DelegatingHandler? I can't seem to figure out how to get access to it, and I figure it's because it executes outside of the controller execution...
Is it possible to refer to it?
No, because message handlers operate on raw HttpRequestMessage or raw HttpResponseMessage (in case of continuations). So really, there is no concept of "current controller executing" with DelegatingHandlers since message handlers will be called before dispatching the request to the controller or (again, in the case of continuations) after the controller returns the reponse.
However, it really depends what you are trying to do.
If you want to know to which controller the request will end up getting routed, you can manually call the mechanism that would internally select the controllers.
public class MyHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var config = GlobalConfiguration.Configuration;
var controllerSelector = new DefaultHttpControllerSelector(config);
// descriptor here will contain information about the controller to which the request will be routed. If it's null (i.e. controller not found), it will throw an exception
var descriptor = controllerSelector.SelectController(request);
// continue
return base.SendAsync(request, cancellationToken);
}
}
Extending the #GalacticBoy solution, it would be better to use
public class MyHandler : DelegatingHandler
{
private static IHttpControllerSelector _controllerSelector = null;
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
if (_controllerSelector == null)
{
var config = request.GetConfiguration();
_controllerSelector = config.Services.GetService(typeof(IHttpControllerSelector)) as IHttpControllerSelector;
}
try
{
// descriptor here will contain information about the controller to which the request will be routed. If it's null (i.e. controller not found), it will throw an exception
var descriptor = _controllerSelector.SelectController(request);
}
catch
{
// controller not found
}
// continue
return base.SendAsync(request, cancellationToken);
}
}
Depending on what your doing with the information maybe your fine with getting the information after the request is executed. For example logging the executed controller/action.
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
namespace Example
{
public class SampleHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
HttpResponseMessage response = task.Result;
string actionName = request.GetActionDescriptor().ActionName;
string controllerName = request.GetActionDescriptor().ControllerDescriptor.ControllerName;
// log action/controller or do something else
return response;
}, cancellationToken);
}
}
}

Exception handling beyond Exception Filters?

Using Asp.net WebApi (RC), how can I catch errors that are not caught by Exception Filters or Application_Error() in global.asax?
With both of these in place it seems that there is a class of exceptions still not covered. For example: ApiControllerActionSelector_AmbiguousMatch error (Multiple actions were found that match the request: {0}).
I'm not specifically concerned about the above error, this error just pointed out that there is a class of errors that aren't being caught by either my Exception Filter or Application_Error method.
So how can I cover all my bases?
You're right, there are several classes of exception not trapped by either Application_Error or ExceptionFilter. The Web API request pipeline is processed separately from the ASP.NET MVC pipeline (at least through MVC 4) so the MVC Application_Error doesn't kick-in. Also, if your application throws HttpResponseException type exceptions, they will not be caught by an ExceptionFilter by design (see the ExceptionFilter paragraph). To access all exceptions thrown by your code, you'll need to create a DelegatingHandler along the lines of this code:
public class ResponseExceptionTrapper : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
return base
.SendAsync(request, cancellationToken)
.ContinueWith(response =>
{
var result = response.Result;
if (!result.IsSuccessStatusCode)
{
var exceptionResult = string.Format(
"Response exception: Path({0}) Status({1}) ",
request.RequestUri,
result.StatusCode);
if (result.Content != null)
{
var exceptionReadTask =
result.Content.ReadAsStringAsync();
exceptionReadTask.Wait();
exceptionResult += "Message:\n\r" +
exceptionReadTask.Result;
}
// Do something appropriate with exceptionResult
}
return result;
}, cancellationToken);
}
}
You can wire up the handler with this line in your global config logic:
GlobalConfiguration.Configuration.MessageHandlers.Add(
new ResponseExceptionTrapper());
I believe that Exception Filters only get called once the action is invoked (in which case there is a try/catch around it). The Ambiguous match error would pop up before that in the pipeline and there could be other errors that pop up after that (e.g. a formatter error) as you mention.
I'm not sure you can have one solution to address all of the aspects (since the hosting implementation can vary), but you could try overriding the HttpControllerDispatcher. This class is one of the "root" classes used in the pipeline. Specifically, you could override SendAsync to do your try/catch and handle accordingly.

Resources