I need to create rest style web service with one of the methods conforming to this type of URI.
api/v1/{controller}/{device}/registrations/{id}
public class DevicesController : ApiController
{
public void Get(string device, string id)
{
}
}
Seems like I'm missing something out there, any ideas on how to handle this?
Figured this out, must have been doing something wrong before. This seems to work fine.
routes.MapHttpRoute("PassApi",
"api/v1/{controller}/{device}/registrations/{id}",
new { controller = "Devices" });
Related
Trying to make a flexible way for documentation in .net core swagger when using shared code between services.
Please have a look at this example:
public abstract class Err
{
/// <summary>
/// The error x
/// </summary>
public abstract string Code { get; }
}
public class Err1 : Err
{
public override string Code { get => "100"; }
}
public class Err2 : Err
{
public override string Code { get => "200"; }
}
public class Err3 : Err
{
public override string Code { get => "300"; }
}
[Route("api/test")]
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Err), StatusCodes.Status400BadRequest)]
[ApiController]
public class TestController : ControllerBase
{
[HttpGet]
public ActionResult Get()
{
int i = (new Random().Next(1, 11));
if (1 < 4)
return BadRequest(new Err1());
if (i < 7)
return BadRequest(new Err2());
return Ok("OK");
}
}
This will of course produce a swagger looking something like this:
What I would like to have instead is something like this:
So the important thing here is that the code=300 is NOT included in the summary, because it is not being used in this assembly.
So what I've been thinking of here is;
Is it possible to do some reflection, find all inherited classes being used and write that to the XML (while compiling?) instead of the ordinary summary part?
Override how Swagger read the summary and write your own code.
Can you hook up some Swagger middleware to handle this so it does not read from the XML?
If the reflection part is not possible, of course the next best thing would be in some way to hardcode a list of all the Error-classes being used for this particular services.
Any ideas here guys?
So,
for the first part, what I understand it is not possible to find out if a class is used or not via reflection :(
And for the second part, if you replace the swashbuckle with nswag you are actually able to have this nice "one of" -feature in the swagger UI :) But in my case that is not enough so I guess this is not possible right now.
So far I tried searching for an answer, what I found was something query string related, custom model binders or something with the viewbag (irrelevant). Being still pretty new to C# and not very familiar with its internal workings such as this, so I don't feel I can make sense of the answers or translate them into this situation. (Like this) With this question I'm hoping to find an answer with comment or a few of explanation.
The situation:
I want to test if the model binding attributes are configured correctly. Main motivation is TDD, a specific configuration is required and needs to be test driven. Another motivation is that if someone messes with it and breaks it, the test will immediately tell where the problem is.
Example Controller with a single endpoint (the route has to be that way):
[ApiController]
[Route("/cats")]
public class CatsController : ControllerBase
{
[HttpGet]
[Route("cat-breed/{cat_breed}/cat-size/{cat_size}")]
public IActionResult GetMultipleCats([FromRoute] GetCatsRequestObject request)
{
//implementation is not important
return Ok();
}
}
A request object to encapsulate input fields (one of our standards):
public class GetCatsRequestObject
{
[FromRoute(Name = "cat_breed")] public string CatBreed { get; set; }
[FromRoute(Name = "cat_size")] public string CatSize { get; set; }
}
What I'm looking for is the test, where I somehow do a call with the url to whatever is doing model binding, and then in the controller I check if the values from the url are in the correct places in/and in my request object. Something like this:
private CatsController _controllerUnderTest = new CatsController();
[Test]
public void GivenSomeUrl_WhenControllerIsCalledWithIt_ThenParametersFromUrlGetBindedCorrectly()
{
//arrange
string urlpart = "/cats/cat-breed/British%20Shorthair/cat-size/Medium%20Large";
GetCatsRequestObject expectedResult = new GetCatsRequestObject() { CatBreed = "British Shorthair", CatSize = "Medium Large" };
// The rest of the setup I don't know how to do
//act
// somehow do a call with the url, rather than request object.
//assert
// somehow capture the model binding result and compare it with expected result.
}
Is this possible to test? How would you do this? I realize this is a lengthy post, so I thank you for taking your time to read this.
I am attempting to verify that a user is authorized via a custom policy. I followed the tutorial at Ode To Code to add this functionality to my controller. From within Visual Studio, the code appears to be correct and utilizing a known overload.
Notice that it says that the overload is an "extension". I didn't take much notice of this until I spent 5 hours today trying to solve the following error:
As you can see, it would appear that the overload I'm attempting to use isn't being utilized. Am I doing something wrong here? Is there something special I have to do to include these extended methods? I've attempted cleaning and rebuilding the solution but this hasn't solved the problem.
While you've defined the field for IAuthorizationSerivce, you haven't provided any way for that to be set. You need to define a constructor for the LRController that takes a single parameter of IAuthorizationService, and assign that to the field.
I think there was a definition of that constructor in the tutorial.
Please note the name change: such as the global variable name for IAuthorizationService _authorization has been prefixed with an underscore. Obviously not required, but as a good rule of thumb/good coding standard, IMO. :-)
public class LRController : Controller
{
private readonly IAuthorizationService _authorization;
// you're missing this constructor & this pattern is known as Constructor Dependency Injection
public LRController(IAuthorizationService authorization)
{
_authorization = authorization;
}
public async Task<RedirectToActionResult> Index()
{
var superAdmin = await _authorization.AuthorizeAsync(User, "IsLucky");
//rest of your code here
}
}
EDIT
Additionally, if you wanted/needed to inject other interfaces into this controller, you would add it to that LRController constructor. Would look something like this:
public class LRController : Controller
{
private readonly IAuthorizationService _authorization;
private readonly IOtherService _otherService;
public LRController(IAuthorizationService authorization, IOtherService otherService)
{
_authorization = authorization;
_otherService = otherService;
}
public async Task<RedirectToActionResult> Index()
{
var superAdmin = await _authorization.AuthorizeAsync(User, "IsLucky");
}
public async Task Foo()
{
await _otherService.Bar();
}
}
I just looked into ActionFilters and they are quite useful. Now, I tried having more than one decorating a method, so as to separate the logic. I thought this would be useful.
So here's an example method
[Common.PortalSecurity.Login]
[Common.PortalSecurity.UserRole]
public HttpResponseMessage GetAll(string sessionToken)
{
return new HttpResponseMessage();
}
This works fine, but it is mandatory that Login should execute before UserRole.
Is it 100% the order of execution will be respected at every request ?
This blog post seems to say it should work.
Any ideas ?
I had the solution proposed above working as such:
Your custom attributes have to inherit:
public class LoginAttribute : ActionFilterWithOrderAttribute
{
}
public class UserRoleAttribute : ActionFilterWithOrderAttribute
{
}
And a method wanting to use it should be decorated as:
[Common.PortalSecurity.Login(Order=1)]
[Common.PortalSecurity.UserRole(Order=2)]
public HttpResponseMessage GetAll(string sessionToken)
{
return new HttpResponseMessage();
}
In MVC5 you inherit from ActionFilter and indicate the order (using Order property of ActionFilter) in the custom attribute like this:
[Common.PortalSecurity.Login(Order=1)]
[Common.PortalSecurity.UserRole(Order=2)]
public HttpResponseMessage GetAll(string sessionToken)
{
return new HttpResponseMessage();
}
You can get more information at: https://msdn.microsoft.com/en-us/library/system.web.mvc.filterattribute.order(v=vs.118).aspx
I'm trying to implement custom paging with the EntitySetController.
public class MyController : EntitySetController<Poco, int>
{
public IQueryable<Poco> Get()
{
var result = _myBusinessLogic.Search(QueryOptions.Top.Value);
return result.AsQueryable()
}
}
I think I'm missing something because it looks like the controller is trying to apply paging to the results of the Search method that already returns just one page. How can I prevent it from doing that and apply paging myself?
It looks like I can just inherit from ODataController instead and implement:
public IEnumerable<Poco> Get(ODataQueryOptions odataQueryOptions)
but I was wondering if I can stay with EntitySetController so that there's less plumbing code to write.
I wanted to stick to the OData format and not return PageResult<>
You can only take full control of the query using ODataQueryOptions or let the framework handle it completely for you using QueryableAttribute. There is no middle ground unfortunately.
So, I think doing an ODataController is the right way to solve this now.
That said, I can suggest a dirty workaround that would work for the time being. Beware that this relies on internal implementations that might/will change and break you.
public class MyController : EntitySetController<Poco, int>
{
public IQueryable<Poco> Get()
{
var result = _myBusinessLogic.Search(QueryOptions.Top.Value);
RemoveQueryString(Request, "$top");
return result.AsQueryable()
}
// This method relies that code that looks for query strings uses the extension
// method Request.GetQueryNameValuePairs that relies on cached implementation to
// not parse request uri again and again.
public static void RemoveQueryString(HttpRequestMessage request, string name)
{
request.Properties[HttpPropertyKeys.RequestQueryNameValuePairsKey] = request.GetQueryNameValuePairs().Where(kvp => kvp.Key != name);
}
}