I have a WebAPI project that is included as a dependency to a frontend React+Redux project.
I created a code first database with a controller exposing some HttpGet functions where the functions modify a DbContext database.
When the app runs the Startup.ConfigureServices is not called and the webapi calls from React gets me this error:
"SyntaxError: Unexpected token < in JSON at position 0"
WebAPI functions from a different controller which don't use DBcontext stuff work though..
In LinkController I have the following:
[Route("api/[controller]")]
public class LinkController : Controller
{
[HttpGet("[action]")]
public URLSubmitResult SubmitLink(string url)
{
return new URLSubmitResult() { Success = true };
}
public class URLSubmitResult
{
public bool Success { get; set; }
}
}
private readonly LinkDbContext _context;
public LinkController(LinkDbContext context)
{
_context = context;
}
In ValuesController the following:
[Route("api/[controller]")]
public class ValuesController : Controller
{
[HttpGet("[action]")]
public URLSubmitResult SubmitLink(string url)
{
return new URLSubmitResult() { Success = true };
}
public class URLSubmitResult
{
public bool Success { get; set; }
}
public ValuesController()
{
}
The call is not happening if I go api/link/submitlink?url=lol.com but it does if it's api/values/submitlink?url=lol.com
The actual issue, regardless of the error I'm receiving in the response, is that api/link/submit link is not resulting in the controller function being called.
Most likely scenario is an undefined result is coming back which isn't typecasting to json.
So basically the issue was that the referenced WebAPI assembly was not adding the appropriate service to the Dependency injection.
In Startup.ConfigureServices for the main project I added:
services.AddDbContext<LinkDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("LinkDBConnectionString")));
Along with the connection string (LinkDBConnectionString) the dependency injection worked and the WebAPI initialized.
From some more experience I've found this project setup is not really what I want which was to decouple the frontend from the WebAPI, including the WebAPI as a dependency to the frontend is not the answer in the case of web.
Instead I now run the WebAPI as its own thing in Visual Studio and do calls to it through the frontend.
Related
I've started a new project using .Net 5 (my previous was .Net Framework 4.7). I'm writing a web API project and I want all my controllers/action responses to be of a certain type. This allows me to put some info I want included in every response, such as the current user info (and more stuff too).
My generic response looks like this (I've only left the relevant code):
public class MyResponse<T>
{
public T data { get; set; }
public User user { get; set; }
public MyResponse(T inputData)
{
data = inputData;
}
}
And I set the response on a controller's action this way:
public IActionResult Get()
{
var response = new MyResponse<string>("Hello");
return Ok(response);
}
So the idea is that the response always contains a "data" property with the actual data, and a bunch of other properties with metadata.
The problem is how to include information on the logged in user in .Net 5. In .Net 4.x you could just access HttpContext from anywhere, so you could just populate the User property. But this is not possible in .Net 5
I'm going crazy trying to understand how to achieve this in .Net 5.
The first thing I've tried is DI (which I'm new to, so I might not be understanding this properly).
The first thing I tried is to make my User class depend on IHttpContextAccessor as most documentation points to:
public class User : IIdentity
{
private readonly IHttpContextAccessor _httpContextAccessor;
public User(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
}
and register it this way on startup.cs:
services.AddHttpContextAccessor();
services.AddTransient<User>();
But that doesn't work well, since when I try to create my User class within MyResponse class:
var user = new User(); // This doesn't work, as the constructor requires one argument
So the constructor requires one argument so I can't create the class like that. I (believe) I would need to create the User from the DI container, but I don't have access to that on MyResponse class (or at least I couldn't really understand how to do it or if possible at all).
I could pass the HttpContext from the controller to MyResponse, but that seems plain wrong (plus, there might be other people writing controllers, so I think it's better if they don't explicitly need to pass that to the response, should be handled transparently)
My concrete questions:
Any thoughts of how can I get hold of the HttpContext within my custom response class?
Should I be looking for an alternative option (such as a Middleware or Filter) to generate my response?
Thank you very much.
You could use a factory along with dependency injection.
Create your user class:
using Microsoft.AspNetCore.Http;
using System.Security.Principal;
public class User : IIdentity
{
private IHttpContextAccessor HttpContextAccessor { get; }
public User(IHttpContextAccessor httpContextAccessor)
{
this.HttpContextAccessor = httpContextAccessor;
}
public string AuthenticationType => this.HttpContextAccessor.HttpContext.User.Identity.AuthenticationType;
public bool IsAuthenticated => this.HttpContextAccessor.HttpContext.User.Identity.IsAuthenticated;
public string Name => this.HttpContextAccessor.HttpContext.User.Identity.Name;
}
Use DI to inject factories with the types you want:
services.AddHttpContextAccessor();
services.AddSingleton(a => GetResponse<string>(a));
services.AddSingleton(a => GetResponse<int>(a));
services.AddSingleton(a => GetResponse<decimal>(a));
Func<T, MyResponse<T>> GetResponse<T>(IServiceProvider serviceProvider)
{
var contextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
var user = new User(contextAccessor);
return (data) => new MyResponse<T>(user, data);
}
Then inject it where you want:
namespace WebAppFiles.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
private Func<int, MyResponse<int>> ResponseFactory { get; }
public MyController(Func<int, MyResponse<int>> responseFactory)
{
this.ResponseFactory = responseFactory;
}
[HttpGet]
public IActionResult Get([FromQuery] int value)
{
return Ok(this.ResponseFactory(value));
}
}
}
I am creating sample project based on DDD.
I created SharedKernel project where I have my class for DomainEvents
public static class DomainEvents
{
public static IContainer Container { get; set; }
static DomainEvents()
{
Container = StructureMap.Container.For<GenericTypesScanning>();
}
public static void Raise<T>(T args) where T : IDomainEvent
{
foreach (var handler in Container.GetAllInstances<IHandle<T>>())
{
handler.Handle(args);
}
}
}
and this is class GenericTypesScanning
public class GenericTypesScanning : Registry
{
public GenericTypesScanning()
{
Scan(scan =>
{
// 1. Declare which assemblies to scan
scan.Assembly("MyLib");
// 2. Built in registration conventions
scan.AddAllTypesOf(typeof(IHandle<>));
scan.WithDefaultConventions();
});
}
}
In MyLib project I have class AppointmentConfirmedEvent and handler for this event:
public class EmailConfirmationHandler: IHandle<AppointmentConfirmedEvent>
{
public void Handle(AppointmentConfirmedEvent appointmentConfirmedEvent)
{
// TBD
}
}
I have temporary rest api controller where I wanted to check if everything is correctly registered and I am doing this:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET: api/<ValuesController>
[HttpGet]
public IEnumerable<string> Get()
{
var appointmentConfirmedEvent = new AppointmentConfirmedEvent();
DomainEvents.Raise(appointmentConfirmedEvent);
return new string[] { "value1", "value2" };
}
}
but when DomainEvents.Raise is called the event is not handled because internal call Container.GetAllInstances<IHandle<T>>() returns empty array.
I did analogous example with Console app and there everything works fine. Any idea why it does not work in case of ASP.NET Core .NET 5 project?
-Jacek
The AddAllTypesOf() method does not work with open generics. See the ConnectImplementationsToTypesClosing() method in the StructureMap docs: http://structuremap.github.io/generics/
And just a reminder, StructureMap is no longer supported. Moreover, 2.6.4.1 was the "haunted" version of StructureMap that was admittedly buggy.
The first thing to do is to check out the type scanning diagnostics:
http://structuremap.github.io/diagnostics/type-scanning/.
The type scanning can be a little brittle if there are missing assemblies. The diagnostics might point out where things are going wrong. Also, try your WhatDoIHave() diagnostics too.
And also, just making sure that you know that StructureMap is no longer supported and has been replaced by Lamar:
https://jeremydmiller.com/2018/01/29/sunsetting-structuremap/
https://jasperfx.github.io/lamar
I have two Web APIs built using .Net Core 3.1
I am trying to pass a model which has 2 different Lists as properties.
Though the method in the 2nd API is called successfully from the 1st API, the properties of the model i.e. lists are null.
There is no issue with the API call as i could pass and receive the value when i called a method with a string as input parameter.
Here is my code
1st API method :
public async Task SomeMethod(MyModel Input, string resource)
{
var client = new ServiceClient(BaseUrl)
{
Resource = resource,
};
client.Post(Input);
}
resource here is the url for 2nd API Method
Model :
public class MyModel
{
public List<Class1> List1;
public List<Class2> List2;
}
2nd API:
[Route("v1/myservice")]
[ApiController]
public class MyController : ControllerBase
{
private readonly IMyHandler _myHandler;
private readonly ILogger _logger;
private string _MyDestinationPath;
public MyController (IMyHandler myHandler, IConfiguration configuration, ILogger logger)
{
_myHandler= myHandler;
_MyDestinationPath = configuration.GetValue<string>("DestinationPath");
_logger = logger;
}
[HttpPost, Route("API2Method"), ResponseType(typeof(string))]
public async Task API2Method([FromBody]MyModel Input)
{
if(!ModelState.IsValid)
{
return;
}
}
}
Can someone point out what i need to change to receive the lists i am passing to the 2nd API instead of null
We have already answer for this question, Please refer the following urls for this issue.
https://stackoverflow.com/questions/24874490/pass-multiple-complex-objects-to-a-post-put-web-api-method
For more help on parameter passing, Please refer this basic article
https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api#using-frombody
I'm building a Web API using NET Core + Autofac.
This is my BaseRepository which gets appsettings.json values via injection:
public class BaseRepository
{
protected string ConnectionString { get; }
public BaseRepository(IOptions<CustomSettings> customConfig)
{
ConnectionString = customConfig.Value.ConnectionString;
}
}
This is how I configure IOptions:
services.Configure<CustomSettings>(Configuration.GetSection("CustomSettings"));
I need to validate the content of a header through an Stored Procedure which is invoked within a Repository. I want to do it by using an ActionFilter instead of in every controller's method.
public class ValidateRequestAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var token = context.HttpContext.Request.Headers["Authorization"];
var isValidToken =
new TokenRepository(_customSettings, _cache).ValidToken(token); // this is an error as both parameters do not exist at this moment
if (!isValidToken)
context.Result = new ForbidResult();
}
}
My plan is to invoke the filter in every controller like this:
[ValidateRequest]
[Route("api/[controller]")]
public class ValuesController : Controller
I haven't found a way to inject _customSettings (IOptions) nor _cache (IMemoryCache) to my custom ActionFilter. After reading, I'm starting to think it won't be possible.
Another option would be to access appsettings.json from my BaseRepository?
I have started a new Web API project that requires that we switch the database the application is running to based on HTTP header information sent to the API. The application user will be identified by a HTTP header and the application should then change to use their database.
I have a base controller CrudControllerBase<T> ( to handle simple generic HTTP requests ) which creates a DataService<T> in it's constructor. All of my controllers will derive from this base controller and will have access to this DataService. The DataService is used to do common DB queries ( FindById(), FindAll(), etc. ) and more complex queries are bolted on using extension methods.
public abstract class CrudControllerBase<T> : ApiController where T : class, IEntity
{
protected IDataService<T> _dataService;
public CrudControllerBase()
{
this._dataService = new DataService<T>();
}
[HttpGet]
public virtual async Task<IHttpActionResult> Get(Guid id)
{
var model = await _dataService.FindByIdAsync(id);
return Ok<T>(model);
}
//code left out
}
public class OrdersController : CrudControllerBase<OrderItem>
{
}
and in the DataService I new up the DbContext class:
public class DataService<T> : IDataService<T> where T:class, IEntity
{
private readonly AppDbContext _context;
public DataService()
{
_context = new AppDbContext(); // need to pass in connection string
}
// code left out
}
I need to be able to pass in the connection string to the constructor of AppDbContext but in the constructor of CrudControllerBase I do not have access to the HttpRequestMessage to be able to pass this info to the DataService.
Can anyone suggest a solution ? I am quite happy to try a completely different way of doing this if someone can suggest something. Thanks !
OK so I have got this working. It may not be the best solution but it works and if anyone has any feedback / improvements then please share. Thanks to #AaronLS for pointing me in the right direction. This article also helped a lot.
The first step was to create a CustomControllerFactory that implements the IHttpControllerActivator interface. This gives you a Create method in which you can write your own code for newing up your Controllers. This is my CustomControllerFactory where I new up my controller passing in the HTTP Header as a string:
public class CustomControllerFactory : IHttpControllerActivator
{
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var schemaKey = request.Headers.Where(k => k.Key == "schema").FirstOrDefault().Value.FirstOrDefault();
return (IHttpController)Activator.CreateInstance(controllerType, new string[] { schemaKey });
}
}
The next step is to tell the web API to use this method for instantiating controllers. To do this I added this line to my WebApiConfig class:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new CustomControllerFactory());
The last thing I needed to was add a constructor to each controller which took in the string value and passed it to the base controller
public OrdersController(String databaseName) : base(databaseName) { }
and my base controller passes the parameter to the DataService
public CrudControllerBase(String databaseName)
{
this._dataService = new DataService<T>(databaseName);
}
and my database passes in the connection string to the AppDbContext() constructor
public DataService(String databaseName)
{
this._context = new AppDbContext(BuildConnectionString(databaseName));
}
I realise there is no error handling / security checking yet but I will add that in :-)