Validation Filter in Azure Function - c#

Problem
I am using fluent validation for model validation and I want it to be done by ValidationFilter in azure function. I already did it in asp.net core app but I don't know how to do it in azure functions
Code
MyValidator
using FluentValidation;
namespace MyLocker.Models.Validations
{
public class PortfolioFolderVMValidator : AbstractValidator<PortfolioFolderVM>
{
public PortfolioFolderVMValidator()
{
RuleFor(reg => reg.Title).NotEmpty().WithName("Title").WithMessage("{PropertyName} is required");
RuleFor(reg => reg.UserId).NotEmpty().WithName("UserId").WithMessage("{PropertyName} is required");
}
}
}
Validate Model Action Filter
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Mvc.Filters;
using MyLocker.Models.Common;
namespace MyLocker.WebAPI.Attributes
{
public sealed class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var errors = context.ModelState.Keys.SelectMany(key => context.ModelState[key].Errors.Select(error => new ValidationError(key, error.ErrorMessage))).ToList();
context.Result = new ContentActionResult<IList<ValidationError>>(HttpStatusCode.BadRequest, errors, "BAD REQUEST", null);
}
}
}
}
Startup
after that add it in startup.cs class like this
services.AddMvc(opt => opt.Filters.Add(typeof(ValidateModelAttribute)))
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<PortfolioFileModelValidator>())
I tried it in azure function but there are different classes and interface in it.

At present , there are 2 types of filter are available with Azure function:
FunctionInvocationFilterAttribute
As the name indicates, Filter used to execute PRE and POST processing logic when target job function is invoked.
FunctionExceptionFilterAttribute
Exception Filter will be called whenever there is an exception thrown by the Azure Function.
But you can write a wrapper on top of Azure function which will help you write the clean code.
For the wrapper based implementation , you can refer below code repo:
https://gist.github.com/bruceharrison1984/e5a6aa726ce726b15958f29267bd9385#file-fluentvalidation-validator-cs
Also alternatively you can implement the pipeline for validation the data model passed to azure function. You can find the complete reference in below repo:
https://github.com/kevbite/AzureFunctions.GreenPipes/tree/master/src/AzureFunctions.GreenPipes
Out of these 2 approach, Wrappers is more cleaner and easier to implement.
Let me know if you need any other help.

Related

Is there a way to document how an endpoint is protected using swaggerUI?

Heyho!
I have a quite big application with some endpoints and authorization schemes.
To protect them i created 3 AuthorizeAttributes for rights, rights in a different tenant and parameters, to check them before accessing the endpoint itself. and there are even more.
Also i created authorization policies e.g. that the user needs to be listed in the requested users properties.
Now it would be really helpful for testing, documenting and developing if our swagger UI could list those attributes and policies that apply for the endpoint. is there any way to do this?
As a Framework i'M using microsoft MVC, so controllers all inherit from Microsoft.AspNetCore.Mvc.ControllerBase
as for the swagger package I'm using:
Swashbuckle.AspNetCore.Swagger version 6.3.1. (SwaggerGen/SwaggerUi)
Assuming ASP.NET Core and Swashbuckle, you can use a MarkupFilter to provide additional information in the Swagger UI:
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;
using Microsoft.AspNetCore.Authorization;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Tool
{
internal class AuthorizationMarkupFilter : IOperationFilter
{
private static ImmutableHashSet<string> GetPolicies(MethodInfo methodInfo)
{
var actionAttributes = methodInfo.GetCustomAttributes<AuthorizeAttribute>(true);
var controllerAttributes = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes<AuthorizeAttribute>(true);
var result = ImmutableHashSet<string>.Empty;
foreach (var attribute in actionAttributes.Union(controllerAttributes))
{
if (!string.IsNullOrWhiteSpace(attribute.Policy))
{
result = result.Add(attribute.Policy);
}
}
return result;
}
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var policies = GetPolicies(context.MethodInfo);
if (policies.Count > 0)
{
var sb = new StringBuilder();
sb.AppendLine("<div>");
sb.AppendLine("<b>Authorization:</b>");
sb.AppendLine("<ul>");
foreach (string policy in policies.OrderBy(s => s))
{
sb.Append("<li>").Append(HttpUtility.HtmlEncode(policy)).AppendLine("</li>");
}
sb.AppendLine("</ul>");
sb.AppendLine("</div>");
operation.Description += sb.ToString();
}
}
}
}
And then inside your "Startup" class:
services.AddSwaggerGen(c =>
{
// ...
c.OperationFilter<AuthorizationMarkupFilter>();
}
I've ripped this out of some bigger solution, so I hope it compiles. But you get the picture, I hope.

Configuring ASP.NET Core razor page filter using model.Filters.Add<TFilterType>()

I can setup an ASP.NET Core razor page filter in startup that has dependency injection just fine using <<TType>>() rather than new Type():
services.AddMvc(options =>
{
options.Filters.Add<Filters.AdminPageFilter>();
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddHttpContextAccessor();
This works fine, but would like to apply this to a directory doing something like:
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AddFolderApplicationModelConvention(
"/Admin",
model => model.Filters.Add<Filters.AdminPageFilter>()
);
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
Compilation complains:
Error CS7036 There is no argument given that corresponds to the required formal parameter 'configureSource' of 'ConfigurationExtensions.Add(IConfigurationBuilder, Action)' Startup.cs 71 Active
Is there a way to Just specify the type via model.Filters.Add<Filters.AdminPageFilter>() and not create an new Instance via model.Filters.Add(new Filters.AdminPageFilter())?
TypeFilterAttribute can help with this. It operates as a filter factory that can generate your filter using dependency injection. It implements IFilterMetadata, so it can be added to model.Filters in the place of your AdminPageFilter.
Here's an extension method that will give you the same .Add<FilterType> functionality:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
public static void Add<TFilterType>(this ICollection<IFilterMetadata> filters) where TFilterType : IFilterMetadata {
var typeFilterAttribute = new TypeFilterAttribute(typeof(TFilterType));
filters.Add(typeFilterAttribute);
}

Creating custom model validation attribute

I'm trying to emulate the behavior of the [ApiController] attribute for model validation, however I'd like to return a JSON object I've made with the validation errors in an Error array within the JSON.
The challenge I'm facing is that I'm unsure how to access validation errors from within the Attribute and I'd like to use the attribute at the class level, so it will run on all controller methods without need of supplying the attribute for each action.
Any direction would be much appreciated.
edit: linked duplicate is how to create custom attribute. I'm looking how to access model validation errors from within an attribute.
I was able to figure out my issue. I was able to utilize ModelState.IsValid in an OnActionExecuting method to access the errors. Unfortunately I'm not familiar enough with making a class level attribute so I have to apply this to all post/patch methods in order for it to work. If someone else comes up with a way to do that easily, let me know!
Project.Structure is for formatting JSON for those curious.
using System;
using System.Collections.Generic;
using System.Linq;
using Project.Structure;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Project.Attributes
{
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var errorList = new List<string>();
foreach (var modelError in context.ModelState.Values)
{
errorList.AddRange(modelError.Errors.Select(error => error.ErrorMessage));
}
var response = new ResponseDto<object>
{
Success = false,
TransactionId = Guid.NewGuid().ToString(),
ResponseType = ResponseType.Operation.Description(),
Response = null,
Errors = errorList,
Warnings = null
};
context.Result = new BadRequestObjectResult(response);
}
}
}
}

No type was found that matches the controller named ***

I am working on an ASP.NET MVC app using oData 4. Up until yesterday they project was in VB.NET. We are now finally attempting to move from VB.NET to C#. I've tried to introduce 2 controllers written in C# into the project and in both instances, receive the dreaded "No type was found that matches the controller named ***" error when attempting to access either controller.
The first one was a new controller. It looks like this:
using System.Data.Entity.Infrastructure;
using System.Net;
using System.Web.Http;
using System.Web.OData;
namespace Controllers
{
public class ORDER_LINE_SUMMARYController : ODataController
{
private Entities db = new Entities();
[EnableQuery()]
public IQueryable<ORDER_LINE_SUMMARY> GetORDER_LINE_SUMMARY()
{
return db.ORDER_LINE_SUMMARY;
}
[EnableQuery()]
public SingleResult<ORDER_LINE_SUMMARY> GetORDER_LINE_SUMMARY([FromODataUri()] decimal key)
{
return SingleResult.Create(db.ORDER_LINE_SUMMARY.Where(ORDER_LINE_SUMMARY => ORDER_LINE_SUMMARY.ID == key));
}
protected override void Dispose(bool disposing)
{
if ((disposing))
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool ORDER_LINE_SUMMARYExists(decimal key)
{
return db.ORDER_LINE_SUMMARY.Count(e => e.ID == key) > 0;
}
}
}
In order to make certain that I didn't miss a step in incorporating this new controller, I also tried replacing an existing VB.NET controller with a C# controller and receive the same error. Except for the entity name, the code is essentially identical to the one above, so I'm not positing it here for brevity's sake.
One interesting note, and I think this is germane to the underlying problem. When I added the new C# controller to replace the existing VB.NET one I hadn't yet removed the VB.NET controller from the project. Obviously the class names for both are the same, however; the .NET compiler didn't error or otherwise complain about this. I suspect this issue may be related to attempting to mix C# and VB.NET within the same assembly due to this.
Any suggestions or ideas would be greatly appreciated. TIA.

Using Swagger/Swashbuckle with a namespace versioned ASP.NET WebAPI

I have an ASP.NET Web API app that I have versioned using namespace versioning.
For example, this method is accessible at api/v1/Location/Process:
namespace Project.Controllers.V1
{
public class LocationController : BaseController
{
[HttpsRequired, HttpGet]
public async Task<ApiResponse> Process(string sessionToken, string id)
Swagger is set up to deal with this setup using the MultipleApiVersions method:
c.MultipleApiVersions((apiDesc, version) => {
var path = apiDesc.RelativePath.Split('/');
var pathVersion = path[1];
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(pathVersion, version, CompareOptions.IgnoreCase) >= 0;
},
vc => {
vc.Version("v1", "Foundbite Client API V1");
});
However when the Swagger UI is generated the methods have the full namespace and controller name in the url: api/V1.Location/Process
Anyone know how I can edit the paths Swagger will use so I can create the correct url? (Basically just need to replace that fullstop). I ideally don't want to use routing attributes.
(There is a similar question to mine here but it misses some keen points that I'd like to address.)

Categories

Resources