Controller Action Method is not getting called when custom routebase class is used - asp.net-mvc-3

Following is the Custom RouteBase class
public class CustomRoutingBehaviour:RouteBase
{
string[] allowedUrl;
public CustomRoutingBehaviour(string[] url)
{
allowedUrl = url;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData route = null;
string browsedurl = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if(allowedUrl.Contains(browsedurl,StringComparer.OrdinalIgnoreCase))
{
route = new RouteData(this,new MvcRouteHandler());
route.Values.Add("controller","CustomiziedController");
route.Values.Add("action","Index");
route.Values.Add("BrowsedURL",browsedurl);
}
return route;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
Following is the routing configuration:
routes.Add(new CustomRoutingBehaviour(new string[] { "~/Customizied/Index", "~/Customizied/Index1" }));

I had exactly the same problem. I have resolved adding an ending slash (/) at url address in the browser. Pay attention to handle the slash in your code. You can add the final slash at every url of the array you pass in ctor of your CustomRoutingBehaviour class or substring browsedurl variable.
I don't know why this way it works.

Related

Conditional validation based on request route with asp.net core 2.2 and FluentValidation

So Basically i wrote a validator for my class with FluentValidation and also a filter to do the validation task for me in my webAPI project, so far it's OK but assume that my User class has firstname,lastname,email,password properties
and i have two routes (one for register and the other one for login)
and as you might have noticed required properties are different on these route.
Thus,should I really need to write individual validation for each and every action i have?because this makes a lot of code code duplication and it's hard to change.is there any way to just add required condition based on the request coming with single validation class?
Any suggestion???
A better practice would be to use a factory pattern for your validations and use a an action filter to short circuit bad requests. You could validate any action argument(Headers, Request Bodies, etc..) with something like this.
public class TestValidationAttribute : Attribute, IActionFilter
{
private string _requestModelName;
public TestValidationAttribute(string requestModelName)
{
_requestModelName = requestModelName;
}
public void OnActionExecuting(ActionExecutingContext context)
{
// using Microsoft.Extensions.DependencyInjection;
var services = context.HttpContext.RequestServices;
var accessor = services.GetService<IHttpContextAccessor>();
var factory = services.GetService<ITestValidatorFactory>();
var tokens = accessor.HttpContext.GetRouteData().DataTokens;
if (!tokens.TryGetValue("RouteName", out var routeNameObj))
{
throw new Exception($"Action doesn't have a named route.");
}
var routeName = routeNameObj.ToString();
var validator = factory.Create(routeName);
if (!context.ActionArguments.TryGetValue(_requestModelName, out var model))
{
throw new Exception($"Action doesn't have argument named {_requestModelName}.");
}
TestModel test;
try
{
test = (TestModel) model;
}
catch (InvalidCastException)
{
throw new Exception($"Action argument can't be casted to {nameof(TestModel)}.");
}
var validation = validator.Validate(test);
if (!validation.Successful)
{
context.Result = new BadRequestObjectResult(validation.ResponseModel);
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
public class TestController : Controller
{
[HttpPost]
[Route("Test/{id}", Name = "TestGet")]
[TestValidation("model")]
public IActionResult Test(TestModel model)
{
return Ok();
}
}
public class ValidationResult
{
public bool Successful { get; }
public ResponseModel ResponseModel { get; }
}
public class TestModel
{
}
public interface ITestValidator
{
ValidationResult Validate(TestModel model);
}
public interface ITestValidatorFactory
{
ITestValidator Create(string routeName);
}

Access TempData in ExecuteResult Asp.Net MVC Core

I wanted to save notification in TempData and shown to user. I create extension methods for this and implement a class which Extends from ActionResult. I need to access TempData in override ExecuteResult method with ActionContext.
Extension Method:
public static IActionResult WithSuccess(this ActionResult result, string message)
{
return new AlertDecoratorResult(result, "alert-success", message);
}
Extends ActionResult class.
public class AlertDecoratorResult : ActionResult
{
public ActionResult InnerResult { get; set; }
public string AlertClass { get; set; }
public string Message { get; set; }
public AlertDecoratorResult(ActionResult innerResult, string alertClass, string message)
{
InnerResult = innerResult;
AlertClass = alertClass;
Message = message;
}
public override void ExecuteResult(ActionContext context)
{
ITempDataDictionary tempData = context.HttpContext.RequestServices.GetService(typeof(ITempDataDictionary)) as ITempDataDictionary;
var alerts = tempData.GetAlert();
alerts.Add(new Alert(AlertClass, Message));
InnerResult.ExecuteResult(context);
}
}
Call extension method from controller
return RedirectToAction("Index").WithSuccess("Category Created!");
I get 'TempData ' null , How can I access 'TempData' in 'ExecuteResult' method.
I was literally trying to do the exact same thing today (have we seen the same Pluralsight course? ;-) ) and your question led me to find how to access the TempData (thanks!).
When debugging I found that my override on ExecuteResult was never called, which led me to try the new async version instead. And that worked!
What you need to do is override ExecuteResultAsync instead:
public override async Task ExecuteResultAsync(ActionContext context)
{
ITempDataDictionaryFactory factory = context.HttpContext.RequestServices.GetService(typeof(ITempDataDictionaryFactory)) as ITempDataDictionaryFactory;
ITempDataDictionary tempData = factory.GetTempData(context.HttpContext);
var alerts = tempData.GetAlert();
alerts.Add(new Alert(AlertClass, Message));
await InnerResult.ExecuteResultAsync(context);
}
However, I have not fully understood why the async method is called as the controller is not async... Need to do some reading on that...
I find out the way to get the TempData. It need to get from ITempDataDictionaryFactory
var factory = context.HttpContext.RequestServices.GetService(typeof(ITempDataDictionaryFactory)) as ITempDataDictionaryFactory;
var tempData = factory.GetTempData(context.HttpContext);

WebApi: method to return a simple string

One of my methods need to return just a simple string of text. What should be the return type of the method? Do I still declare it in a subclass of ApiController?
I tried the following but it does not work:
public class TestController : ApiController
{
public string Announcements()
{
return "Testing abc";
}
}
By default, Web API will send the string as a JSON. However, you can manually force it to return just the text itself and accompany it with the appropriate content type:
public class TestController : ApiController
{
public HttpResponseMessage Announcements()
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent("Testing abc");
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
return response;
}
}

Validation on parameters to a WebAPI action

I have a WebAPI action in which I expect an argument to be provided to match a particular regex (or other validation attribute, say [Range(1, 20)]). The parameter provided also happens to be part of the URL.
[Route("api/stuff/{id}")]
public HttpResponseMessage Get([RegularExpression(#"^\d+-[a-z]{3}$")] id) {
if (!ModelState.IsValid) {
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
//do stuff
}
Unfortunately the model state is always valid, even when the value doesn't match the regex.
How would I do validation of this URL segment?
As recommended by #Pratik you can route constraints are the simplest/easiest way to go about it.
I made a Validation attribute like so:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class IPAddressAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
dynamic address = validationContext.ObjectInstance;
long? scope = address.ScopeId;
var isValid = IPAddresses.IsValid(value as string, scope);
var result = ValidationResult.Success;
if (!isValid)
result = new ValidationResult("The provided IP Address is not a valid IPv4 or IPv6 address");
return result;
}
}
And then can consume that from a route constraint:
public class IPAddressRouteConstraint : IHttpRouteConstraint {
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
object value;
if (!values.TryGetValue(parameterName, out value))
return false;
var attr = new IPAddressAttribute();
return attr.IsValid(value);
}
}
This way it's shared as both in the route and at the model validation (depending where I need the logic).
Should it get that far? Tried this...
[Route("api/stuff/{id:regex(^\d+-[a-z]{3}$)}")]

Put, Delete... Method not allowed in Orchard

I've created WebApi controller based on following tutorial: sebastienros website
My modules name is Company.Accounts.
public class AccountController : ApiController
{
[HttpPost]
public string LogIn([FromBody] UserModel user)
{
// this is working
return this.accountService.LogIn(user.UserName, user.Password);
}
[HttpPut]
public string SomePuthMethod([FromBody] UserModel user)
{
// method not allowed
// some code...
}
}
Implementation of IHttpRouteProvider looks like:
private IEnumerable<RouteDescriptor> GetAccountRoute()
{
yield return new HttpRouteDescriptor
{
Name = "Account",
Priority = 10,
RouteTemplate = "Api/Account",
Defaults = new
{
area = "Company.Accounts",
controller = "Account"
}
};
}
Unfortunately, everything except GET and POST *is not working*. I'm getting simple
Method not allowed.
What's wrong? My Orchard version is 1.7.1.
You put them in the MethodNames public HttpResponseMessage Post([FromBody]...){}

Resources