EFCore Include function works in Get endpoint but does not work in Put endpoint - ef-core-3.1

Hi I am working with IdentityServer4 and EFCore 3.1.0 recently and have a very weird issue with the following two pieces of code. The result is that in ApiResourcesGetByIdController, apiResource in GetById function includes the value of scope and secret. However in ApiResourcesUpdateController, result in Update function does not include the value of scope and secret.
The usage of these two endpoints is that, the frontend calls the GetById endpoint to get ApiResource object and makes some modification in frontend. Then the frontend calls Update endpoint to update the object in database.
Can anyone help to see what could be wrong? Thank you
[Authorize]
[Route("ApiResources")]
[ApiController]
public class ApiResourcesGetByIdController : ControllerBase
{
private readonly ConfigurationDbContext _configurationDbContext;
public ApiResourcesGetByIdController(
ConfigurationDbContext configurationDbContext)
{
_configurationDbContext = configurationDbContext;
}
[HttpGet]
[Route("")]
public IActionResult GetById([FromQuery]int id)
{
ApiResource apiResource = _configurationDbContext.ApiResources
.Include(apiResource => apiResource.Scopes)
.Include(apiResource => apiResource.Secrets)
.FirstOrDefault(apiResource => apiResource.Id == id);
}
}
[Authorize]
[Route("ApiResources")]
[ApiController]
public class ApiResourcesUpdateController : ControllerBase
{
private readonly ConfigurationDbContext _configurationDbContext;
public ApiResourcesUpdateController(
ConfigurationDbContext configurationDbContext)
{
_configurationDbContext = configurationDbContext;
}
[HttpPut]
[Route("")]
public IActionResult Update([FromBody] ApiResource resource )
{
ApiResource result = _configurationDbContext.ApiResources
.Include(apiResource => apiResource.Scopes)
.Include(apiResource => apiResource.Secrets)
.FirstOrDefault(ar => ar.Id == resource.Id);
return Ok()
}
}

Not sure why but I deleted the file and recreated it. Then it works by itself.

Related

ASP.NET CORE 5 API controller not working

Added an API controller to the project and it does not work. I get 404.
[Route("api/hlth")]
[ApiController]
public class hlth : ControllerBase
{
// GET: api/<hlth>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<hlth>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
}
Turns out I need to add
app.MapControllers();
that for some reason is not included in the default project configuration.

HttpGetAttribute doesn't work in core web api

Well known situation. I need two endpoints
GetAll -> api/brands
GetById -> api/brands/1
[ApiController]
[Route("api/[controller]")]
public class BrandsController : ControllerBase
{
private readonly BrandRepository repository;
public BrandsController(BrandRepository repository)
{
this.repository = repository;
}
[HttpGet("{id:int}")]
public async Task<ActionResult> GetById(int id)
{
var brand = await repository.FindAsync(id);
if (brand == null)
{
return NotFound();
}
return Ok(brand);
}
[HttpGet("")]
public ActionResult<IEnumerable<Brand>> GetAll()
{
var brands = repository.GetAll().ToList();
return Ok(brands);
}}
So, I always get into GetAll()
Any ideas? Help, please :)
Is it a correct namespace?
using Microsoft.AspNetCore.Mvc;
for
[HttpGet]
Startup.cs
namespace BackOffice
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<ApplicationDbContext>(
options =>
options.UseMySql(Configuration.GetConnectionString("local")));
services.AddTransient<BrandRepository, BrandRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(
endpoints =>
{
endpoints.MapControllers();
});
app.UseCors();
}
}
}
dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
Change your the attribute on your GetAll action to simply [HttpGet] and then change the attribute on your GetById action to [HttpGet("{id}")] .
You can use a constraint to id if need be but in your case I don't see any need for it. Generally you can use constraints when you have multiple actions on the same route but with different parameter types. For example, "api/brands/1" to get by integer ID and then maybe you have another action that is mapped to "api/brands/gucci" that will search for the brand by string name. Then you can use the {id:int} and {id:string} constraints in your route template to define which action to invoke.
Also make sure you use IActionResult when declaring the action return types. You don't want to use the concrete ActionResult type. Code samples below.
For the GetById action :
[HttpGet("{id}")]
public async Task<IActionResult> GetById(int id)
{
var brand = await repository.FindAsync(id);
if (brand == null)
{
return NotFound();
}
return Ok(brand);
}
For your GetAll action :
[HttpGet]
public IActionResult<IEnumerable<Brand>> GetAll()
{
var brands = repository.GetAll().ToList();
return Ok(brands);
}
This will tell the routing middleware which action to invoke. For actions that you want mapped to the base controller route (i.e. "api/brands"), just use the attribute without an overload. Such as [HttpGet], [HttpPost], [HttpDelete]. For the actions that have a route parameter then you can use [HttpGet("{id}")] and so forth depending on the HTTP method. Don't worry about defining the type of the parameter in the attribute route template. You define the parameter in your action's parameters. For instance:
[HttpGet("{id}")]
public async Task<IActionResult> GetById(int id)
{
// Code here
return Ok();
}
If you want to map a route to something like "api/brands/designers/2" then you would use a template like [HttpGet("designers/{id}")] to do so. Don't put a "/" before the designers.
Edit : Forgot to mention, make sure your Startup.cs is properly configured for Web API routing. You can read the specifics on the ASP.NET Core 3.1 docs for what all the different options do. If you used the Web API template then it's probably fine but it's worth double checking as improperly configured endpoint routing can cause issues. Make sure you have the following in your Configure method in Startup.cs.
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
Make sure that app.UseRouting(); is called before app.UseEndpoints();

Security Claims in DBCommandInterceptor

i am currently using EF and .NET Core 3 through Radzen to build an application. This is working fine, but I want to add additional logging to the database. In order to do so, I would like to make use of the DBCommandInterceptor as shown here to do some post query commands.
Is it possible to get the Claims of the Microsoft Authorization in this Interceptor class? In my normal controller class, I can simply call
var userId = User.FindFirst(ClaimTypes.Name).Value;
This doesn't work in the Interceptor and to be honest, my knowledge about that framework is very poor. I cannot even tell you why I can access the User reference in my ObjectController against the DBCommandInterceptor
If you add the Interceptor in DbContext.OnConfiguring, you can pass any state to it you want.
So require your DbContext to accept an Identity, or with a service dependency it can use to access the user. something like:
public class Db : DbContext
{
ClaimsIdentity user;
public Db(ClaimsIdentity user)
{
this.user = user;
}
Then configure the Interceptor to accept the User, or the DbContext instance. eg
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=localhost;Database=EFCore3Test;Integrated Security = true", a => a.UseRelationalNulls(true))
.ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuting, LogLevel.Information)))
.UseLoggerFactory(MyLoggerFactory)
.AddInterceptors(new MyCommandInterceptor(this));
base.OnConfiguring(optionsBuilder);
}
And have the interceptor use the constructor argument:
public class MyCommandInterceptor : DbCommandInterceptor
{
private Db db;
public MyCommandInterceptor(Db db)
{
this.db = db;
}
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
var userId = db.user.FindFirst(ClaimTypes.Name).Value;
//. . .
return base.ReaderExecuting(command, eventData, result);
}
}

Attribute routing with default root parameter in WebAPI

I have an API where all methods need a fixed parameter {customer} :
/cust/{customerId}/purchases
/cust/{customerId}/invoices
/cust/{customerId}/whatever*
How can I map all controllers to receive this parameter by default in a reusable way like:
endpoints.MapControllerRoute(name: "Default", pattern: "/cust/{customerId:int}/{controller}*"
I am using .net core 3.0 with the new .useEndpoints method on Startup.
You could create an implementation of ControllerModelConvention to custom the attribute route behavior. For more details, see official docs.
For example, suppose you want to combine an attribute route convention (like /cust/{customerId:int}/[controller]) with the existing attribute globally, simply create a Convention as below:
public class FixedCustomIdControllerConvention : IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
var customerRouteModel= new AttributeRouteModel(){
Template="/cust/{customerId:int}",
};
var isApiController= controller.ControllerType.CustomAttributes.Select(c => c.AttributeType)
.Any(a => a == typeof(ApiControllerAttribute));
foreach (var selector in controller.Selectors)
{
if(!isApiController)
{
var oldAttributeRouteModel=selector.AttributeRouteModel;
var newAttributeRouteModel= oldAttributeRouteModel;
if(oldAttributeRouteModel != null){
newAttributeRouteModel= AttributeRouteModel.CombineAttributeRouteModel(customerRouteModel, oldAttributeRouteModel);
}
selector.AttributeRouteModel=newAttributeRouteModel;
} else{
// ApiController won't honor the by-convention route
// so I just replace the template
var oldTemplate = selector.AttributeRouteModel.Template;
if(! oldTemplate.StartsWith("/") ){
selector.AttributeRouteModel.Template= customerRouteModel.Template + "/" + oldTemplate;
}
}
}
}
}
And then register it in Startup:
services.AddControllersWithViews(opts =>{
opts.Conventions.Add(new FixedCustomIdControllerConvention());
});
Demo
Suppose we have a ValuesController:
[Route("[controller]")]
public class ValuesController : Controller
{
[HttpGet]
public IActionResult Get(int customerId)
{
return Json(new {customerId});
}
[HttpPost("opq")]
public IActionResult Post(int customerId)
{
return Json(new {customerId});
}
[HttpPost("/rst")]
public IActionResult PostRst(int customerId)
{
return Json(new {customerId});
}
}
After registering the above FixedCustomIdControllerConvention, the routing behavior is:
The HTTP Request GET https://localhost:5001/cust/123/values will match the Get(int customerId) method.
The HTTP Request POST https://localhost:5001/cust/123/values/opq will match the Post(int customerId) method
Because we intentionally put a leading slash within /rst, the global convention is passed by. As a result, the POST https://localhost:5001/rst will match the PostRst(int customerId) method( with customId=0)
In case you're using a controller annotated with [ApiController]:
[ApiController]
[Route("[controller]")]
public class ApiValuesController : ControllerBase
{
[HttpGet]
public IActionResult Get([FromRoute]int customerId)
{
return new JsonResult(new {customerId});
}
[HttpPost("opq")]
public IActionResult Post([FromRoute]int customerId)
{
return new JsonResult(new {customerId});
}
[HttpPost("/apirst")]
public IActionResult PostRst([FromRoute]int customerId)
{
return new JsonResult(new {customerId});
}
}
You probably need decorate the parameter from routes with [FromRoute].

How do I resolve a WebAPI dependency in Autofac that requires a parameter from the route?

I am struggling with wiring dependencies through autofac in my WebApi 2 project. I have a following interface and class that i'd like to inject in my GET and POST controller actions,
public interface IRepository
{
IContext Context
{
get;
}
void SomeOperation();
}
public MyRepository : IRepository
{
IContext _context;
public MyRepository(IContext context)
{
_context = context;
}
public Context
{
get
{
return _context;
}
}
public void SomeOperation
{
// Perform some operation using _context;
}
}
I 'd like IRepository to be injected in controller like this,
public class MyController : ApiController
{
private readonly IRepository _repo;
public ApplicationsController(IRepository repo)
{
_repo = repo;
}
// GET: api/v1/Contexts({contextId})
public IHttpActionResult Get(string contextId)
{
_repo.SomeOperation();
}
}
IContext object to be injected in MyRepository has to be fetched from a factory, something like this
public class ContextFactory
{
Hashtable contextMap;
IContext Get(string contextId)
{
if contextMap.Contains(contextId)
return contextMap[contextId].Value;
else
{
IContextConfiguration configuration = ContextConfigurationFactory.Get(contextId);
IContext context = new ConcreteContext(configuration);
contextMap.Add[contextId, context];
return context;
}
}
}
I am not sure how to wire all the classes and convert logic in factory classes by injecting relationships through Autofac so that context id passed in url is passed to ContextConfigurationFactory.Get and instantiate ConcreteContext object when not found in hash and eventually Autofac injecting right context object in MyRepository before passing it on to Get action in the controller.
Let's simplify this a bit. What you're trying to do is:
Get the context ID from a route parameter.
Use that route parameter in the factory to create a context.
The rest seems pretty much peripheral - the repository, the controller, all that. The crux of the question is that you need to get a route parameter into your factory.
Given that, let's put together some simplified code:
public class ContextFactory
{
public IContext Get(string contextId)
{
return new Context(contextId);
}
}
public interface IContext
{
string Id { get; }
}
public class Context : IContext
{
public Context(string id)
{
this.Id = id;
}
public string Id { get; private set; }
}
That's basically what you have:
An IContext interface that things need.
A ContextFactory that is basically responsible for building these things.
A Context concrete implementation of IContext that is built by the factory.
I would probably do something like this:
var builder = new ContainerBuilder();
builder.RegisterType<ContextFactory>();
builder.Register(ctx =>
{
var routeData = HttpContext.Current.Request.RequestContext.RouteData;
var id = routeData.Values["contextId"] as string;
var factory = ctx.Resolve<ContextFactory>();
return factory.Get(id);
}).As<IContext>()
.InstancePerLifetimeScope();
Now when you resolve IContext it will use your factory, get the current context ID from route data, and pass it through the factory.
I will leave the following for you to look into:
What happens if the route parameter isn't there? (Autofac won't let you return null.)
What happens if the route parameter has invalid data?
The route parameter is pretty hackable, is this a security risk?
...and so on.