I have a SpringBoot server that offers multiple services under multiple REST controllers. So I have something like:
/serviceA/api1
/serviceA/api2
/serviceB/api1
/serviceB/api2
I want to use 1 Feign client to access them. But I don't really put all these calls in 1 'flat' interface. So I do not want to have:
public interface Client {
#RequestLine(value="GET /serviceA/api1")
public String getServiceAapi1();
#RequestLine(value="GET /serviceA/api2")
public String getServiceBapi2();
#RequestLine(value="GET /serviceB/api1")
public String getServiceAapi1();
#RequestLine(value="GET /serviceB/api2")
public String getServiceBapi2();
}
I'm looking for a way so that at runtime I can use:
client.serviceA().api1();
client.serviceA().api2();
client.serviceB().api1();
client.serviceB().api2();
Any way to do this?
I hope this request makes sense ;-).
Thanks.
Henry
You can use the param option and with java 8, the interfaces are enabled to use the default method, so you could implement something like next:
#RequestLine(value="GET /serviceA/{api}")
public String getServiceApi(#Param("api") String api);
default String api1() {
return getServiceApi("api1");
}
default String api2() {
return getServiceApi("api2");
}
The structure you want cannot be created by the feign client at runtime, you have to create a structure like next by yourself:
public class Client {
private final ServiceA serviceA;
private final ServiceB serviceB;
public Client(ServiceA serviceA, ServiceB serviceB) {
this.serviceA = serviceA;
this.serviceB = serviceB;
}
public ServiceA serviceA() {
return serviceA;
}
public ServiceB serviceB() {
return serviceB;
}
interface ServiceA {
#RequestLine(value="GET /serviceA/api1")
public String api1();
#RequestLine(value="GET /serviceA/api2")
public String api2();
}
interface ServiceB {
#RequestLine(value="GET /serviceB/api1")
public String api1();
#RequestLine(value="GET /serviceB/api2")
public String api2();
}
}
Related
Very new to DI and concepts so I'm struggling to find a solution to the following:
We have a Web Project (MVC, Core 3) and a Class Library (for all the business and data layers). We are trying to have a DBContext Class in the Class Library to handle all the DB connections (using connection string). We can DI into this DBContext the IConfiguration so we can extract the connection string from this and place into a local readonly string. We have a method to return the SQL Connection using this connection string.
The issue is when we are in a business class, we need to access the DBContext class to obtain a SQL Connection object. We cannot create a "new" DBContext as we do not have the IConfigration in the other business classes.
DBContext class library:
public class DBContext
{
private readonly string _connectionString;
private readonly IConfiguration _configuration;
public DBContext(IConfiguration configuration)
{
_configuration = configuration;
_connectionString = configuration.GetConnectionString("db");
}
public SqlConnection Connection()
{
return new System.Data.SqlClient.SqlConnection(_connectionString);
}
}
So the problem lies when we look at the Customer class in the Class Library:
Customer Class:
public class Customer
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public Customer(int customerId)
{
//load customer from the db from the id
using (IDbConnection db = new DBContext().Connection())
{
//call SQL Stored Procedure here ....
}
}
}
We are unable to create a "new" DBContext to access the connection, as in the Customer Class we don't have the IConfiguration object to pass to the constructor.
How do we achieve this the "correct" way?
Is it as bad as having to DI the IConfiguration object into every single class library constructor e.g. Customer from all the web controllers, so we can pass it over to the DBContext? As this seems long winded.
Sorry if this is very basic DI stuff but just struggling to find a good example on how to do this stuff.
Thanks in advance,
Ro
public class Customer
{
private readonly ApplicationDbContext _context;
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public Customer(ApplicationDbContext context, int customerId)
{
_context = context; //use _context to reference the db context.
//do stuff
}
}
And in Startup.cs -> ConfigureServices() method:
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DevDb")));
Edit for Dapper:
In appsettings.json:
"ConnectionStrings": {
"DefaultConnection": "<connection string here>"
}
Then add a class called ConnectionString in the solution. We will use this class to hold the connection string value from the configuration file and use it in our classes via dependency injection.
public sealed class ConnectionString
{
public ConnectionString(string value) => Value = value;
public string Value { get; }
}
Then Register the configuration in the ASP.NET Core dependency injection container in Startup.cs
var connectionString = new ConnectionString(Configuration.GetConnectionString("DefaultConnection"));
services.AddSingleton(connectionString);
Then add your repository class:
using Dapper;
public class MovieRepository
{
private readonly ConnectionString _connectionString;
public MovieRepository(ConnectionString connectionString)
{
_connectionString = connectionString;
}
public async Task<IEnumerable<MovieModel>> GetAllMovies()
{
const string query = #"SELECT m.Id, m.Name,d.Name AS DirectorName, m.ReleaseYear
FROM Movies m
INNER JOIN Directors d
ON m.DirectorId = d.Id";
using (var conn = new SqlConnection(_connectionString.Value))
{
var result = await conn.QueryAsync<MovieModel>(query);
return result;
}
}
}
Then Register the Repository class in you DI container. Add the following code to ConfigureServices method.
services.AddScoped<MovieRepository>();
Add the controller class MoviesController.cs and write an action method to fetch all movies using this MovieRepository.
[ApiController]
public class MoviesController : ControllerBase
{
private readonly MovieRepository _movieRepository;
public MoviesController(MovieRepository movieRepository)
{
_movieRepository = movieRepository;
}
[HttpGet("api/movies")]
public async Task<IActionResult> GetMovies()
{
return Ok(await _movieRepository.GetAllMovies());
}
}
In this example, your ApplicationDbContext is equivalent to the movie repository. Answer from: https://www.c-sharpcorner.com/article/using-dapper-for-data-access-in-asp-net-core-applications/
I have a Mongo - Java MVC Spring 4 connectivity. My insert operations work only once, they don't do a second insert in the collection. What could be the problem?
Here's my code.
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection="subject")
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int id;
private String name;
private String email;
private String address;
private String telephone;
strong text
...
public class EmployeeDAOImpl implements EmployeeDAO {
MongoTemplate mongoTemplate;
public MongoTemplate getMongoTemplate() {
return mongoTemplate;
}
public void setMongoTemplate(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
#Override
public void addEmployee(Employee employee) {
mongoTemplate.save(employee);;
}
#Override
public List<Employee> getAllEmployees() {
// TODO Auto-generated method stub
return mongoTemplate.findAll(Employee.class);
}
#GeneratedValue(strategy=GenerationType.SEQUENCE) annotation is for JPA and not for Mongo.
Make sure that you import the #Id from the mongo package and ID should be auto generated by Mongo.
The first insert works because int has default value of 0 and the second time it tries to insert with the same key.
if you want to have custom ids generated, here is a great tutorial for that: https://www.baeldung.com/spring-boot-mongodb-auto-generated-field
I'm creating a WCF service:
[ServiceContract]
public interface IContractManagementServices
{
...
}
and i want the contract implementation to also implement another interface:
public interface ILogin
{
bool GetLoginCredential(String LoginID, String Password);
}
For example:
public class ContractManagementServices : IContractManagementServices, ILogin
{
...
}
But I am getting an error.
Try this code
private ILogin _businessLogic {get; set;}
public Service()
{
if (_businessLogic == null) { _businessLogic = new Login(); }
}
I think it will solve your problem.
suppose this JAX-RS method :
#GET
#Path("/{id}")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Employee get(#PathParam("id") Long id) {
return myService.findbyId(id);
}
With the following POJO:
#XmlRootElement
public class Employee {
Integer id;
String name; (getters;setters etc...)
}
#XmlRootElement(name="employee")
public class SepcialEmployee extends Employee {
Skill skill; (getters;setters etc...)
}
#XmlRootElement(name="employee")
public class Manager extends Employee {
String headOffice; (getters;setters etc...)
}
This works fine with RESTeasy/spring-MVC integration. And if I call the method from a web browser; I can get the following answer for i.e.:
<employee Id="17">
<name>Marc</name>
<headOffice>accounting</headOffice>
</employee>
But if I use the RESTeasy Client Framework for my unit test. the client proxy generated unmarsalles only the Employee Parent class and I loose the child informations (Manager.headOffice or SepcialEmployee.Skill). Below an extract of my Junit test:
public class Test {
#Path("empl")
public interface EmpProxy {
#GET
#Produces(MediaType.APPLICATION_XML)
Employee getEmployee(#ClientURI String uri);
}
private static TJWSEmbeddedSpringMVCServer server;
public static final String host = "http://localhost:8080/";
public static final int port = 8080;
private static EmpProxy proxy;
#BeforeClass
public static void setup() {
server = new TJWSEmbeddedSpringMVCServer("classpath:test-dispatcher-servlet.xml", port);
server.start();
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target(host);
proxy = target.proxy(EmpProxy.class);
}
#Test
public void test(){
String url = host+"/empl/17";
Employee employee = proxy.getEmployee(url);
System.out.println(employee);
}
}
I have these classes
public class Application {
public String name;
public String ico;
public List<MenuStruct> menu =new ArrayList<MenuStruct>();
//Constructor
public Application() { }
}
public class MenuStruct {
public String id;
public String type;
public String parent;
public String name;
public String secId;
//Constructor
public MenuStruct() {}
}
If I try to deserialize a collection directly in this way:
ApplicationManager apm= new ApplicationManager();
s="[ {\"name\":\"reg_salida\" , \"ico\":\"document-open-2-32x32.ico\" }]";
apm.apps=(new Gson()).fromJson(s,apm.apps.getClass() );
for (Application ap:apm.apps){
System.out.println(ap.name); //gets error here
}
I get a java.lang.ClassCastException.
But if I try to deserialize its containig class ApplicationManager it does not fail.
s="{ \"apps\": [ {\"name\":\"reg_salida\" , \"ico\":\"document-open-2-32x32.ico\" }]}";
ApplicationManager apm=(new Gson()).fromJson(s,ApplicationManager.class);
for (Application ap:apm.apps){
System.out.println(ap.name); // now no errors here! and shows reg_salida
}
Is this a bug of gson 2.2.4? or maybe I am doing something not correct?
Eduard.
You have to provide full definition of property class. Your example should looks like that:
manager.apps = gson.fromJson(json, new TypeToken<List<Application>>() {}.getType());