I have a Spring mvc app and I also have rest services for some functionalities. It was working fine till last week, but suddenly some of the calls are having issues.
I noticed all of these calls are of type "method = RequestMethod.PUT" functions. I get the following error
org.springframework.web.client.HttpClientErrorException: 400 Bad Request
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:614)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:570)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:530)
at org.springframework.web.client.RestTemplate.put(RestTemplate.java:382)
My Controller is
#Controller
#RequestMapping("user/a2")
public class ABCController {
#Autowired
private ABCServices ABCServices;
#RequestMapping(
value = "user/{userName}/checkTest",
method = {RequestMethod.GET, RequestMethod.POST} -----------------this call works fine
)
public #ResponseBody
String check(#PathVariable String userName) {
}
#RequestMapping(
value = "user/{userName}/validateABCSS",
method = RequestMethod.PUT
)
public #ResponseBody
ResponseEntity<byte[]> validateABCSS(#PathVariable String userName, #RequestBody AAA uSS) {
-----------------this call does not work
if ("Success".equals(result)) {
return new ResponseEntity<byte[]>(HttpStatus.OK);
} else {
return new ResponseEntity<byte[]>(result.getBytes(), HttpStatus.BAD_REQUEST);
}
}
}
RestClient.java,
public class RestClient {
private RestTemplate restTemplate;
private String UN;
private String serverUrl;
public RestClient() {
restTemplate = new RestTemplate();
}
public RestClient(String pServerUrl, String key, String sec, String pUN) {
ClientHttpRequestInterceptor tokenAuthInterceptor = new TokenAuthHeaderRequestInterceptor(key,sec, pUN);
restTemplate.setInterceptors(Collections.singletonList(tokenAuthInterceptor));
SSLContext sslContext = null;
try {
sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).useTLS().build();
} catch (Exception e) {
}
SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext,
new String[]{"SSLv2Hello","SSLv3", "TLSv1"}, null, new AllowAllHostnameVerifier());
HttpClient httpClient =
HttpClientBuilder.create().setSSLSocketFactory(connectionFactory).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
restTemplate.setRequestFactory(requestFactory);
}
public enum ServiceURLs {
VALIDATE_ABCSS("user/a2/user/{userName}/validateABCSS"), CheckTest("user/a2/user/{userName}/checkTest");
private String id;
private String url;
private ServiceURLs(String id) {
this.id = id;
this.url = id;
}
private ServiceURLs(String id, String url) {
this.id = id;
this.url = url;
}
public String getId() {
return this.id;
}
public String getUrl() {
return this.url;
}
}
public RestTemplate getRestTemplate() {
return this.restTemplate;
}
}
I call the template as below
getRestTemplate().put(serverUrl + RestClient.ServiceURLs.VALIDATE_ABCSS.getUrl(), pObject, getLogin()).
The only thing I did was to config SSL, but now changed them back to old state(though the keystores has the keys). I am even using the URLS with only http. Any help appreciated. Thanks.
The setup: my app is in tomcat, rest services app is in weblogic, the authentication happens via custom app(keys and secret).
May be result is coming as false.
Check what is reason for result value is "false".
Related
I have created a SpringBoot project with Jhipster. The database I am using is MongoDB.
In the application-dev.yml I have the following configuration:
data:
mongodb:
uri: mongodb://<user>:<pass>#<ip>:<port>
database: gateway
The user, password, ip Address, and port, in my application-dev are real values.
The DatabaseConfiguration.java is:
#Configuration
#EnableMongoRepositories("es.second.cdti.repository")
#Profile("!" + JHipsterConstants.SPRING_PROFILE_CLOUD)
#Import(value = MongoAutoConfiguration.class)
#EnableMongoAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class DatabaseConfiguration {
private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(DateToZonedDateTimeConverter.INSTANCE);
converters.add(ZonedDateTimeToDateConverter.INSTANCE);
return new MongoCustomConversions(converters);
}
#Bean
public Mongobee mongobee(MongoClient mongoClient, MongoTemplate mongoTemplate, MongoProperties mongoProperties) {
log.debug("Configuring Mongobee");
Mongobee mongobee = new Mongobee(mongoClient);
mongobee.setDbName(mongoProperties.getMongoClientDatabase());
mongobee.setMongoTemplate(mongoTemplate);
// package to scan for migrations
mongobee.setChangeLogsScanPackage("es.second.cdti.config.dbmigrations");
mongobee.setEnabled(true);
return mongobee;
}}
The CloudDatabaseConfiguration is:
#Configuration
#EnableMongoRepositories("es.second.cdti.repository")
#Profile(JHipsterConstants.SPRING_PROFILE_CLOUD)
public class CloudDatabaseConfiguration extends AbstractCloudConfig {
private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
#Bean
public MongoDbFactory mongoFactory() {
return connectionFactory().mongoDbFactory();
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converterList = new ArrayList<>();
converterList.add(DateToZonedDateTimeConverter.INSTANCE);
converterList.add(ZonedDateTimeToDateConverter.INSTANCE);
converterList.add(DurationToLongConverter.INSTANCE);
return new MongoCustomConversions(converterList);
}
#Bean
public Mongobee mongobee(MongoDbFactory mongoDbFactory, MongoTemplate mongoTemplate, Cloud cloud) {
log.debug("Configuring Cloud Mongobee");
List<ServiceInfo> matchingServiceInfos = cloud.getServiceInfos(MongoDbFactory.class);
if (matchingServiceInfos.size() != 1) {
throw new CloudException("No unique service matching MongoDbFactory found. Expected 1, found "
+ matchingServiceInfos.size());
}
MongoServiceInfo info = (MongoServiceInfo) matchingServiceInfos.get(0);
Mongobee mongobee = new Mongobee(info.getUri());
mongobee.setDbName(mongoDbFactory.getDb().getName());
mongobee.setMongoTemplate(mongoTemplate);
// package to scan for migrations
mongobee.setChangeLogsScanPackage("es.second.cdti.config.dbmigrations");
mongobee.setEnabled(true);
return mongobee;
}
}
The cdtiApp.java is:
#SpringBootApplication
#EnableConfigurationProperties({ApplicationProperties.class})
public class CdtiApp implements InitializingBean{
private static final Logger log = LoggerFactory.getLogger(CdtiApp.class);
private final Environment env;
public CdtiApp(Environment env) {
this.env = env;
}
/**
* Initializes cdti.
* <p>
* Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile
* <p>
* You can find more information on how profiles work with JHipster on https://www.jhipster.tech/profiles/.
*/
#PostConstruct
public void initApplication() {
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
log.error("You have misconfigured your application! It should not run " +
"with both the 'dev' and 'prod' profiles at the same time.");
}
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
log.error("You have misconfigured your application! It should not " +
"run with both the 'dev' and 'cloud' profiles at the same time.");
}
}
/**
* Main method, used to run the application.
*
* #param args the command line arguments.
*/
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CdtiApp.class);
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
logApplicationStartup(env);
}
private static void logApplicationStartup(Environment env) {
String protocol = "http";
if (env.getProperty("server.ssl.key-store") != null) {
protocol = "https";
}
String serverPort = env.getProperty("server.port");
String contextPath = env.getProperty("server.servlet.context-path");
if (StringUtils.isBlank(contextPath)) {
contextPath = "/";
}
String hostAddress = "localhost";
try {
hostAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.warn("The host name could not be determined, using `localhost` as fallback");
}
log.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\t{}://localhost:{}{}\n\t" +
"External: \t{}://{}:{}{}\n\t" +
"Profile(s): \t{}\n----------------------------------------------------------",
env.getProperty("spring.application.name"),
protocol,
serverPort,
contextPath,
protocol,
hostAddress,
serverPort,
contextPath,
env.getActiveProfiles());
String configServerStatus = env.getProperty("configserver.status");
if (configServerStatus == null) {
configServerStatus = "Not found or not setup for this application";
}
log.info("\n----------------------------------------------------------\n\t" +
"Config Server: \t{}\n----------------------------------------------------------", configServerStatus);
}
#Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
}
The Vehicle entity:
#org.springframework.data.mongodb.core.mapping.Document(collection = "vehicle")
public class Vehicle implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
#NotNull
#Field("plate")
private String plate;
#NotNull
#Field("registrationDate")
private Instant registrationDate;
#NotNull
#Field("brand")
private String brand;
#NotNull
#Field("model")
private String model;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPlate() {
return plate;
}
public void setPlate(String plate) {
this.plate = plate;
}
public Instant getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Instant registrationDate) {
this.registrationDate = registrationDate;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
The VehicleDTO is:
public class VehicleDTO {
private String id;
private String plate;
private Instant registrationDate;
private String brand;
private String model;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPlate() {
return plate;
}
public void setPlate(String plate) {
this.plate = plate;
}
public Instant getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Instant registrationDate) {
this.registrationDate = registrationDate;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
The VehicleMapper is:
#Mapper(componentModel = "spring")
public interface VehicleMapper{
Vehicle toEntity(VehicleDTO source);
VehicleDTO toDto(Vehicle target);
}
The VehicleResource is:
#RestController
#RequestMapping("/api")
#CrossOrigin(origins = "*", methods = { RequestMethod.GET, RequestMethod.POST })
public class VehicleResource {
private final Logger log = LoggerFactory.getLogger(VehicleResource.class);
#Value("${jhipster.clientApp.name}")
private String applicationName;
#Autowired
private final VehicleService vehicleService;
public VehicleResource(VehicleService vehicleService) {
this.vehicleService = vehicleService;
}
#PostMapping("/vehicle")
#PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<Vehicle> createVehicle(#Valid #RequestBody VehicleDTO vehicleDTO) throws URISyntaxException {
log.debug("REST request to save Vehicle : {}", vehicleDTO);
Vehicle newVehicle = vehicleService.createVehicle(vehicleDTO);
return ResponseEntity.created(new URI("/api/vehicle/" + newVehicle.getPlate()))
.headers(HeaderUtil.createAlert(applicationName, "vehicleManagement.created", newVehicle.getPlate()))
.body(newVehicle);
}
}
The VehicleService interface is:
public interface VehicleService {
Vehicle createVehicle(VehicleDTO vehicleDTO);
}
The VehicleServiceImpl is:
#Service
public class VehicleServiceImpl implements VehicleService{
#Autowired
private final VehicleRepository vehicleRepository;
#Autowired
private final VehicleMapper mapper;
public VehicleServiceImpl(VehicleRepository vehicleRepository, VehicleMapper mapper) {
this.vehicleRepository = vehicleRepository;
this.mapper = mapper;
}
private final Logger log = LoggerFactory.getLogger(VehicleServiceImpl.class);
#Override
public Vehicle createVehicle(VehicleDTO vehicleDTO) {
Vehicle vehicle = vehicleRepository.save(mapper.toEntity(vehicleDTO));
log.debug("Created Information for vehicle: {}", vehicle);
return vehicle;
}
}
The VehicleRepository interface is:
/**
* Spring Data MongoDB repository for the {#link Vehicle} entity.
*/
#Repository
public interface VehicleRepository extends MongoRepository<Vehicle, String> {
}
From the Swagger console I access the Vehicle-Resource:
Swagger console
Click on the button and write in the text box the json with the vehicle data:
enter JSON data
As we can see in the following image, the answer is 201. Initially the vehicle was saved with the identifier "id": "60e740935ed5a10e2c2ed19e".
Send request
I access the database to check that the vehicle has been correctly stored in the vehicle table. To my surprise ... there is no vehicle in the vehicle table:
show database
I can make sure that the data in the database application-dev is OK. I don't have any other databases.
I suspect that transactions with the database are not actually being made. This data is somehow stored in memory because if I do a findAllVehicles from Swagger it does return the vehicle.
I have a eureka server running (jhipster-registry) and two microservices that synchronize with it. The Gateway, which acts as a reverse proxy and the Vehiculos microservice. The Swagger console is the gateway, from where I make the request to insert vehicles. Everything seems to work, but as I say in bbdd does not save anything.
I connected my Spring-Boot-Application to a MongoDB. The application is nothing serious, just for getting into working with spring and MongoDB.
The problem it, that my id is a String and I get an Internal Server Error, when I pass the id of a database entry, in order to get it byId...
This is my domain class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Document(collection = "songinfo")
public class SongInfo {
#Id
private String id;
private int songId;
private String songName;
private String description;
}
The Controller-Method:
#RequiredArgsConstructor
#RestController
#RequestMapping("/songsinfo")
public class SongsInfoController {
private final SongInfoService songInfoService;
#GetMapping(value = "/{id}", headers = "Accept=application/json", produces =
{MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<SongInfo> getSongInfoById(#PathVariable(value = "id") String id) {
SongInfo songInfo = songInfoService.getSongInfoById(id);
if (songInfo == null)
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
return new ResponseEntity<>(songInfo, HttpStatus.OK);
}
The SongInfoServiceImpl:*
#Override
public SongInfo getSongInfoById(String id) {
return songInfoRepository.findById(id).orElseThrow(NotFoundException::new);
}
This is the SongsInfoRepository:
public interface SongInfoRepository extends MongoRepository<SongInfo, String> {
}
Getting all songinfos from the database is working fine:
But when is pass the id from one of these entries, I get this:
What is wrong here with my implementation?
You're throwing the exception in SongInfoServiceImpl which is not handled in your SongsInfoController Class.
Solution 1: Instead of throwing the exception return null.
SongInfoServiceImpl.java
#Override
public SongInfo getSongInfoById(String id) {
return songInfoRepository.findById(id).orElse(null);
}
Solution 2: Add try catch block
SongsInfoController.java
#RequiredArgsConstructor
#RestController
#RequestMapping("/songsinfo")
public class SongsInfoController {
private final SongInfoService songInfoService;
#GetMapping(value = "/{id}",
headers = "Accept=application/json",
produces = {MediaType.APPLICATION_JSON_VALUE}
)
public ResponseEntity<SongInfo> getSongInfoById(#PathVariable(value = "id") String id) {
SongInfo songInfo = null;
try {
songInfo = songInfoService.getSongInfoById(id);
} catch(Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(songInfo, HttpStatus.OK);
}
}
I think you need to divide two problem.
Check id parameter SongsInfoController
Inside controller check your parameter is valid through log or sysout
Check getSongInfoById method in SongInfoServiceImpl
Simply getSongInfoById(8752); is get error?
I want to add comment but my reputation is under 50.
If you comment above two solution check result, then I will add additional answer.
#FeignClient(name = "test", url="http://xxxx")
How can I change the feign URL (url="http://xxxx") during the runtime? because the URL can only be determined at run time.
You can add an unannotated URI parameter (that can potentially be determined at runtime) and that will be the base path that will be used for the request. E.g.:
#FeignClient(name = "dummy-name", url = "https://this-is-a-placeholder.com")
public interface MyClient {
#PostMapping(path = "/create")
UserDto createUser(URI baseUrl, #RequestBody UserDto userDto);
}
And then the usage will be:
#Autowired
private MyClient myClient;
...
URI determinedBasePathUri = URI.create("https://my-determined-host.com");
myClient.createUser(determinedBasePathUri, userDto);
This will send a POST request to https://my-determined-host.com/create (source).
Feign has a way to provide the dynamic URLs and endpoints at runtime.
The following steps have to be followed:
In the FeignClient interface we have to remove the URL parameter. We have to use #RequestLine annotation to mention the REST method (GET, PUT, POST, etc.):
#FeignClient(name="customerProfileAdapter")
public interface CustomerProfileAdaptor {
// #RequestMapping(method=RequestMethod.GET, value="/get_all")
#RequestLine("GET")
public List<Customer> getAllCustomers(URI baseUri);
// #RequestMapping(method=RequestMethod.POST, value="/add")
#RequestLine("POST")
public ResponseEntity<CustomerProfileResponse> addCustomer(URI baseUri, Customer customer);
#RequestLine("DELETE")
public ResponseEntity<CustomerProfileResponse> deleteCustomer(URI baseUri, String mobile);
}
In RestController you have to import FeignClientConfiguration
You have to write one RestController constructor with encoder and decoder as parameters.
You need to build the FeignClient with the encoder, decoder.
While calling the FeignClient methods, provide the URI (BaserUrl + endpoint) along with rest call parameters if any.
#RestController
#Import(FeignClientsConfiguration.class)
public class FeignDemoController {
CustomerProfileAdaptor customerProfileAdaptor;
#Autowired
public FeignDemoController(Decoder decoder, Encoder encoder) {
customerProfileAdaptor = Feign.builder().encoder(encoder).decoder(decoder)
.target(Target.EmptyTarget.create(CustomerProfileAdaptor.class));
}
#RequestMapping(value = "/get_all", method = RequestMethod.GET)
public List<Customer> getAllCustomers() throws URISyntaxException {
return customerProfileAdaptor
.getAllCustomers(new URI("http://localhost:8090/customer-profile/get_all"));
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public ResponseEntity<CustomerProfileResponse> addCustomer(#RequestBody Customer customer)
throws URISyntaxException {
return customerProfileAdaptor
.addCustomer(new URI("http://localhost:8090/customer-profile/add"), customer);
}
#RequestMapping(value = "/delete", method = RequestMethod.POST)
public ResponseEntity<CustomerProfileResponse> deleteCustomer(#RequestBody String mobile)
throws URISyntaxException {
return customerProfileAdaptor
.deleteCustomer(new URI("http://localhost:8090/customer-profile/delete"), mobile);
}
}
I don`t know if you use spring depend on multiple profile.
for example: like(dev,beta,prod and so on)
if your depend on different yml or properties. you can define FeignClientlike:(#FeignClient(url = "${feign.client.url.TestUrl}", configuration = FeignConf.class))
then
define
feign:
client:
url:
TestUrl: http://dev:dev
in your application-dev.yml
define
feign:
client:
url:
TestUrl: http://beta:beta
in your application-beta.yml (I prefer yml).
......
thanks god.enjoy.
use feign.Target.EmptyTarget
#Bean
public BotRemoteClient botRemoteClient(){
return Feign.builder().target(Target.EmptyTarget.create(BotRemoteClient.class));
}
public interface BotRemoteClient {
#RequestLine("POST /message")
#Headers("Content-Type: application/json")
BotMessageRs sendMessage(URI url, BotMessageRq message);
}
botRemoteClient.sendMessage(new URI("http://google.com"), rq)
You can create the client manually:
#Import(FeignClientsConfiguration.class)
class FooController {
private FooClient fooClient;
private FooClient adminClient;
#Autowired
public FooController(ResponseEntityDecoder decoder, SpringEncoder encoder, Client client) {
this.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
.target(FooClient.class, "http://PROD-SVC");
this.adminClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
.target(FooClient.class, "http://PROD-SVC");
}
}
From documentation: https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#_creating_feign_clients_manually
In interface you can change url by Spring annotations. The base URI is configured in yml Spring configuration.
#FeignClient(
name = "some.client",
url = "${some.serviceUrl:}",
configuration = FeignClientConfiguration.class
)
public interface SomeClient {
#GetMapping("/metadata/search")
String search(#RequestBody SearchCriteria criteria);
#GetMapping("/files/{id}")
StreamingResponseBody downloadFileById(#PathVariable("id") UUID id);
}
Use #PathVariable like this:
#Service
#FeignClient(name = "otherservicename", decode404 = true)
public interface myService {
#RequestMapping(method = RequestMethod.POST, value = "/basepath/{request-path}")
ResponseEntity<String> getResult(#RequestHeader("Authorization") String token,
#RequestBody HashMap<String, String> reqBody,
#PathVariable(value = "request-path") String requestPath);
}
Then from service, construct the dynamic url path and send the request:
String requestPath = "approve-req";
ResponseEntity<String> responseEntity = myService.getResult(
token, reqBody, requestPath);
Your request url will be at: "/basepath/approve-req"
I prefer to build feign client by configuration to pass a url at run time (in my case i get the url by service name from consul discovery service)
so i extend feign target class as below:
public class DynamicTarget<T> implements Target<T> {
private final CustomLoadBalancer loadBalancer;
private final String serviceId;
private final Class<T> type;
public DynamicTarget(String serviceId, Class<T> type, CustomLoadBalancer loadBalancer) {
this.loadBalancer = loadBalancer;
this.serviceId = serviceId;
this.type = type;
}
#Override
public Class<T> type() {
return type;
}
#Override
public String name() {
return serviceId;
}
#Override
public String url() {
return loadBalancer.getServiceUrl(name());
}
#Override
public Request apply(RequestTemplate requestTemplate) {
requestTemplate.target(url());
return requestTemplate.request();
}
}
var target = new DynamicTarget<>(Services.service_id, ExamsAdapter.class, loadBalancer);
package commxx;
import java.net.URI;
import java.net.URISyntaxException;
import feign.Client;
import feign.Feign;
import feign.RequestLine;
import feign.Retryer;
import feign.Target;
import feign.codec.Encoder;
import feign.codec.Encoder.Default;
import feign.codec.StringDecoder;
public class FeignTest {
public interface someItfs {
#RequestLine("GET")
String getx(URI baseUri);
}
public static void main(String[] args) throws URISyntaxException {
String url = "http://www.baidu.com/s?wd=ddd"; //ok..
someItfs someItfs1 = Feign.builder()
// .logger(new FeignInfoLogger()) // 自定义日志类,继承 feign.Logger
// .logLevel(Logger.Level.BASIC)// 日志级别
// Default(long period, long maxPeriod, int maxAttempts)
.client(new Client.Default(null, null))// 默认 http
.retryer(new Retryer.Default(5000, 5000, 1))// 5s超时,仅1次重试
// .encoder(Encoder)
// .decoder(new StringDecoder())
.target(Target.EmptyTarget.create(someItfs.class));
// String url = "http://localhost:9104/";
//
System.out.println(someItfs1.getx(new URI(url)));
}
}
Say I have the below test case
I want to be able to bind camel case parameters:
anyData.put("my_id", "bob#gmail.com");
How can I get this test to pass??
public class FormBindingExampleTest {
public static class FormBindingExampleModel {
public String myid;
public String email;
public String getMyid() {
return myid;
}
public void setMyid(String myid) {
this.myid = myid;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
#Test
public void itShouldBindForm(){
Form<FormBindingExampleModel> userForm = form(FormBindingExampleModel.class);
Map<String,String> anyData = new HashMap();
anyData.put("my_id", "bob#gmail.com");
anyData.put("email", "secret");
FormBindingExampleModel user = userForm.bind(anyData).get();
System.out.println(user.myid);
assert(user.myid.equals("bob#gmail.com"));
}
}
Use form's fill() method inorder to populate the form with existing value.
#Test
public void itShouldBindForm(){
Form<FormBindingExampleModel> userForm = form(FormBindingExampleModel.class);
FormBindingExampleModel formModel = new FormBindingExampleModel();
formModel.setMyid("bob#gmail.com");
formModel.setEmail("secret");
userForm.fill(formModel);
FormBindingExampleModel user = userForm.get();
System.out.println(user.getMyid);
assert(user.getMyid.equals("bob#gmail.com"));
}
Documentation available here.
I'm trying to add HATEOAS links to a JSON resource served by a Spring REST controller.
I see I should use a resource assembler as described at https://github.com/spring-projects/spring-hateoas
The example displays a Person class and a PersonResource class.
I understand the PersonResource class is defined as:
public class PersonResource extends ResourceSupport {
}
What is then the Person class ? Is it a data domain class ?
In my case, I have defined an Admin class that is a REST domain class, and I specified it as having resource support:
public class Admin extends ResourceSupport {
private String firstname;
private String lastname;
private String email;
private String login;
private String password;
private String passwordSalt;
public Admin() {
}
public String getFirstname() {
return this.firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return this.lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getLogin() {
return this.login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPasswordSalt() {
return passwordSalt;
}
public void setPasswordSalt(String passwordSalt) {
this.passwordSalt = passwordSalt;
}
public EventAdmin toEventAdmin() {
EventAdmin eventAdmin = new EventAdmin();
BeanUtils.copyProperties(this, eventAdmin);
return eventAdmin;
}
public static Admin fromEventAdmin(EventAdmin eventAdmin) {
Admin admin = new Admin();
BeanUtils.copyProperties(eventAdmin, admin);
return admin;
}
}
My REST controller sees only this Admin class as it is a REST domain class. It does not know, and should not know, of anything data domain class.
So I wonder how to use the resource assembler support here.
I don't understand why I should have an additional data domain Admin class here.
kind Regards,
Following Mike's answer here is how my controller now looks like:
#RequestMapping(method = RequestMethod.POST, produces = "application/json; charset=utf-8")
#ResponseBody
public ResponseEntity<Admin> add(#RequestBody Admin admin, UriComponentsBuilder builder) {
AdminCreatedEvent adminCreatedEvent = adminService.add(new CreateAdminEvent(admin.toEventAdmin()));
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", "application/json; charset=utf-8");
responseHeaders.setLocation(builder.path("/admin/{id}").buildAndExpand(adminCreatedEvent.getAdminId()).toUri());
Admin createdAdmin = adminResourceAssembler.toResource(adminCreatedEvent.getEventAdmin());
ResponseEntity<Admin> responseEntity = new ResponseEntity<Admin>(createdAdmin, responseHeaders, HttpStatus.CREATED);
return responseEntity;
}
Before, instead of using the resource assembler I was doing a:
Admin createdAdmin = Admin.fromEventAdmin(adminCreatedEvent.getEventAdmin());
createdAdmin.add(linkTo(methodOn(AdminController.class).add(createdAdmin, builder)).withSelfRel());
But it was not giving me the resource id in the url.
Your ResourceAssembler implementation needs to know about both the data domain class and the REST domain class, because its job is to convert the former to the latter.
If you want to keep knowledge of your data classes out of your controller, you could make a resource conversion service which would retrieve the data from the repo and use a ResourceAssembler to turn it into resources that the controller can know about.
#Component
public class AdminResourceAssembler extends ResourceAssemblerSupport<Admin, AdminResource> {
public AdminResourceAssembler() {
super(AdminController.class, AdminResource.class);
}
public AdminResource toResource(Admin admin) {
AdminResource adminResource = createResourceWithId(admin.getId(), admin); // adds a "self" link
// TODO: copy properties from admin to adminResource
return adminResource;
}
}
#Service
public class AdminResourceService {
#Inject private AdminRepository adminRepository;
#Inject private AdminResourceAssembler adminResourceAssembler;
#Transactional
public AdminResource findOne(Long adminId) {
Admin admin = adminRepository.findOne(adminId);
AdminResource adminResource = adminResourceAssembler.toResource(admin);
return adminResource;
}
}
#Controller
#RequestMapping("/admins")
public class AdminController {
#Inject private AdminResourceService adminResourceService;
#RequestMapping(value="/{adminId}", method=RequestMethod.GET)
public HttpEntity<AdminResource> findOne(#PathVariable("adminId") Long adminId) {
AdminResource adminResource = adminResourceService.findOne(adminId);
return new ReponseEntity<>(adminResource, HttpStatus.OK);
}
}