I am having some trouble with webapi 2.2 routing. I have installed v5.2 into my project and have modified my config to use a Custom DirectRouteProvider
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
(inherit: true);
}
}
Now I have setup a base controller with methods that I want to allow to be overridden for example
[HttpGet]
[Route("")]
[EnableQuery]
public async virtual Task<IHttpActionResult> Get()
{
var data = await DomainService.GetQueryable();
if (data != null)
{
return Ok(data);
}
return NotFound();
}
If I run the project and call an endpoint like http://localhost:3000/somecontroller the above Get() method is called.
If within somecontroller I try
[HttpGet]
[EnableQuery]
[Route("")]
public async override Task<IHttpActionResult> Get()
{
var data = await _skillRepository.ParentSkillsAsync();
if (data != null)
{
return Ok(data);
}
return NotFound();
}
I get an error of
Multiple actions were found that match the request
Is there anyway to do what I am trying here? is frustrating trying to get this to work.
Thank you
Dan - I was having the same trouble myself and here's how I solved it.
I'm not sure if this is the best solution, but it's working for me.
On my route attribute I added the Order property with a value of 3 into the action on the base class.
The default value for the Order property is 0, so the value of the derived class would be 0.
The base class needs to have an Order property that is higher than the derived class.
So, in your example, I believe this would allow your code to work like you want it to:
Action in base class
[HttpGet]
[Route("", Order=3)]
[EnableQuery]
public async virtual Task<IHttpActionResult> Get()
{
var data = await DomainService.GetQueryable();
if (data != null)
{
return Ok(data);
}
return NotFound();
}
Action in derived class:
[HttpGet]
[EnableQuery]
[Route("")]
public async override Task<IHttpActionResult> Get()
{
var data = await _skillRepository.ParentSkillsAsync();
if (data != null)
{
return Ok(data);
}
return NotFound();
}
Related
Does anyone know how I can mark an argument on ActionDescriptor.Parameters to behave in a similar way the [BindNever] is behaving?
I want to always exclude a specific argument from a specific type without keep decorating it on the Controller.
Essentially I would like to be able to add my injected to my functions somehow how similar to the way its done with CancellationToken
public class TestController : ControllerBase
{
[HttpGet(Name = "Get")]
public IActionResult Get([BindNever] IInjectedInterface injected)
{
//Injected can be used in this method
return Ok();
}
[HttpPost(Name = "Post")]
public IActionResult Post([BindNever] IInjectedInterface injected, FormModel formModel)
{
//Injected doesn't work here. There is an error that
/*System.InvalidOperationException: 'Action 'WebApplication3.Controllers.TestController.Post (WebApplication3)'
has more than one parameter that was specified or inferred as bound from request body. Only one parameter per action may be bound from body.
Inspect the following parameters, and use 'FromQueryAttribute' to specify bound from query, 'FromRouteAttribute' to specify bound from route,
and 'FromBodyAttribute' for parameters to be bound from body:
IInjectedInterface injected
FormModel formModel'
*/
return Ok();
}
}
public class ActionExecutionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var injectedParam = context.ActionDescriptor.Parameters.SingleOrDefault(x => x.ParameterType == typeof(IInjectedInterface));
if (injectedParam != null)
{
context.ActionArguments[injectedParam.Name] = new Injected(99);
}
await next.Invoke();
}
private class Injected : IInjectedInterface
{
public Injected(int someData)
{
SomeData = someData;
}
public int SomeData { get; }
}
}
I was able to solve it. Apparently you need to add the following lines on your program.cs to avoid the model binder related errors.
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(IInjectedInterface)));
options.ModelMetadataDetailsProviders.Add(
new BindingSourceMetadataProvider(typeof(IInjectedInterface), BindingSource.Special));
My controllers return unified RequestResult:
public Task<RequestResult> SomeAction()
{
...
return new RequestResult(RequestResultType.NotFound);
}
public class RequestResult
{
public RequestResultType Type { get;set; }
... //actual data
}
public enum RequestResultType
{
Success = 1,
NotFound = 2
}
So basically RequestResult combines actual Action data and error type (if it happened). Now I need to specify Response Type at some point in case if Action returned Error. My best guess here is to use Middleware:
public class ResponseTypeMiddleware
{
private readonly RequestDelegate next;
public ResponseTypeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
var response = context.Response.Body; //how to access object?
}
}
but I can't figure out what to do with it. What I'd perfectly like to do is to check if response is of type RequestResult, then specify ResponseType equal BadRequest. But I don't see how I can do it here as what I have is just a stream. May be I can hijack into pipeline earlier, before result was serialized (Controller?).
P. S. The reason why I don't use Controller.BadRequest directly in Action is that my Action's logic is implemented via CQRS command/query handlers, so I don't have direct access to Controller.
As you are going to process controller's action result (MVC), the best way is to use ActionFilter or ResultFilter here, instead of Middleware. Filters in ASP.NET Core are a part of MVC and so know about controllers, actions and so on. Middleware is a more common conception - it is an additional chain in application request-response pipeline.
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
// get or set controller action result here
var result = context.Result as RequestResult;
}
}
I'm a bit new to test project. I currently have a web api project which contains Get, Put, Post and Delete methods. When comes to writing test cases, I'm confused. Should I write test code to test the Http URL?
My web api code:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
using (var unitOfWork = new UnitOfWork(_db))
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
}
}
And in my test project, I kind of stuck here. We are using Xunit. How to write test code to test the Get method? Or should I write code to test the URL api/values/5 instead, but how?
[Fact]
public void GetTest()
{
using (var unitOfWork = new UnitOfWork(new MockDatabase()))
{
}
}
Any help would be appreciated.
You need to make a couple of changes before you can really unit test your controller. You need to pass an instance of your UnitOfWork class into the controller in its constructor. Then your controller method code becomes:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
Then in your unit tests you do this:
[Fact]
public void GetTest()
{
// Arrange
// You really want to mock your unit of work so you can determine
// what you are going to send back
var unitOfWork = new MockUnitOfWork();
var systemUnderTest = new Controller(unitOfWork);
system.Request = new HttpRequestMessage();
// Act
var result = systemUnderTest.Get(1);
// Assert
// Here you need to verify that you got back the expected result
}
Injecting the UnitOfWork class into the controller is probably another question. Mark Seemann has an excellent post on the subject, but it might be a little advanced. There are a number of different ways to accomplish that with simpler (but maybe not as robust methods). Google is your friend with that. But if you have questions, post another question.
Hope that helps.
You would need to make some design changes to your controller to make it easy to test. In your action you are creating an instances which will make it difficult to test with a fake dependencies to the controller. Also your controller should depend on abstractions rather than concretions which will allow the controller to be more testable.
public class MyWebApiController : ApiController {
private IUnitOfWork unitOfWork;
public MyWebApiController(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id) {
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null) {
return NotFound();
} else {
return Ok(result);
}
}
//...other code
}
Notice the controller uses dependency injection to inject an IUnitOfWork. That makes the controller more testable, because you can inject mocks of its dependencies when unit testing.
From there it is just to create an instance of the controller and call the method under test with mocks of the dependencies.
[Fact]
public void GetTest() {
//Arrange (Setup the parts needed to run test)
var unitOfWork = new MockUnitOfWork(new MockDatabase());
//Or using your mocking framework of choice
//var unitOfWork = Mock.Of<IUnitOfWork>(); //this is using Moq
var controller = new MyWebApiController(unitOfWork);
var id = "Test Id value here";
//Act (call the method under test)
var result - controller.Get(id);
//Assert (check results)
//...Do your assertion pertaining to result of calling method under test
}
Reference : Unit Testing Controllers in ASP.NET Web API 2
I have an Asp.Net Core 1 RC1 application that uses a custom route constraint to control access to the application. The application (hosted on a server running IIS 7.5) is getting intermittent 404 errors which I suspect is caused by this routing constraint. Here you can see a screenshot that shows the intermittent 404 errors:
I suspect that this issue is related to the code that defines the route constraint not being thread-safe. The custom route constraint needs a DbContext because it needs to check in the database if the application is enabled for the brand specified in the route, and I suspect that this DbContext instance could be causing the issue. Here is how the routing is defined in the application:
// Add MVC to the request pipeline.
var appDbContext = app.ApplicationServices.GetRequiredService<AppDbContext>();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "branding",
template: "branding/{brand}/{controller}/{action}/{id?}",
defaults: new { controller="Home", action="Index" },
constraints: new { brand = new BrandingRouteConstraint(appDbContext) });
});
And here is the custom route constraint:
// Custom route constraint
public class BrandingRouteConstraint : IRouteConstraint
{
AppDbContext _appDbContext;
public BrandingRouteConstraint(AppDbContext appDbContext) : base() {
_appDbContext = appDbContext;
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey, IDictionary<string, object> values, RouteDirection routeDirection)
{
if (values.Keys.Contains(routeKey))
{
var whiteLabel = _appDbContext.WhiteLabels.Where(w => w.Url == values[routeKey].ToString()).FirstOrDefault();
if (whiteLabel != null && whiteLabel.EnableApplication != null && (bool)whiteLabel.EnableApplication)
{
return true;
}
}
return false;
}
}
Can anyone confirm that this issue is caused by the code not being thread-safe and recommend a way to change the implementation so that it is thread-safe?
I can't comment on RouteContraint's, haven't used them much, but have you tried Resource Based Authorization instead? Looks like it might be more suited to what you're trying to achieve?
From here and here:
Request authentication service inside your controller
public class DocumentController : Controller
{
IAuthorizationService authorizationService;
public DocumentController(IAuthorizationService authorizationService)
{
this.authorizationService = authorizationService;
}
}
Apply authorization checks in your Action:
public async Task<IActionResult> Edit(Guid documentId)
{
Document document = documentRepository.Find(documentId);
if (document == null)
{
return new HttpNotFoundResult();
}
if (await authorizationService.AuthorizeAsync(User, document, Operations.Edit))
{
return View(document);
}
else
{
return new HttpUnauthorizedResult();
}
}
I've used the OperationAuthorizationRequirement class in the sample, so define this class in your project:
public static class Operations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement { Name = "Create" };
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement { Name = "Read" };
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement { Name = "Update" };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = "Delete" };
}
Implement the authorization handler (using built in OperationAuthorizationRequirement requirement):
public class DocumentAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
protected override void Handle(AuthorizationContext context,
OperationAuthorizationRequirement requirement,
Document resource)
{
// Validate the requirement against the resource and identity.
// Sample just checks "Name"field, put your real logic here :)
if (resource.Name == "Doc1")
context.Succeed(requirement);
else
context.Fail();
}
}
And not forgetting ConfigureServices:
services.AddInstance<IAuthorizationHandler>(
new DocumentAuthorizationHandler());
It's a bit more work, but adds quite a lot of flexibility.
Below is an Example.
public class MyController : Controller
{
[Route("~/api/mycontroller")]
[HttpGet]
public int ID()
{
try
{
return somecontroller.getID(ID);
}
catch (Exception ex)
{
throw ex;
}
}
}
Above it the controller that is fetching the ID from the below controller.
Below is the controller that it is inherited.
public class Controller : ApiController
{
public int ID
{
get
{
return int.Parse(HttpContext.Current.Request.Headers["ID"]);
}
}
}
How do i write unit test case for the following.???
Oh, unit testing HttpContext.Current. That's one of my favorites :-)
You can't write a unit test for something that depends on HttpContext.Current. So if you want to write testable code the first step is to search in your entire solution for the HttpContext.Current keyword and simply wipe them out from existence.
In this particular case you would of course replace them with the corresponding abstraction:
public class Controller : ApiController
{
public int ID
{
get
{
return int.Parse(Request.Headers.GetValues("ID").FirstOrDefault());
}
}
}
Now it's completely trivial to unit test your Web API controller properly:
// arrange
var sut = new MyController();
sut.Request = new HttpRequestMessage();
sut.Request.Headers.TryAddWithoutValidation("ID", "5");
// act
var actual = sut.SomeControllerAction();
// assert
Assert.AreEqual(5, actual);