I am new in the world of Spring Boot and MongoDB so this could be a stupid question.
I created a Spring Boot project linked to a MondoDB database. In the Controller I've defined these methods: get, getAll, add, update and delete.
Everything works fine while I test my app on PostMan, except for the update method. Indeed in PostMan, using the PUT command, I get this error:
"status": 405,
"error": "Method Not Allowed"
Looking for a solution, I found these lines in PostMan:
PUT non allowed
where the value of "Allow"only contains "GET, DELETE" and not PUT.
Maybe this fact is linked to my error? How can I fix it?
Thank you and sorry for my bad english and lack of knowledge of SpringBoot!
EDIT 1: Controller code:
#PutMapping("/{id}")
public ResponseEntity <Cliente> updateCliente(#PathVariable(value = "id") String id, #RequestBody Cliente cliente){
Optional<Cliente> c = clienteRepo.findById(id);
Cliente _c = new Cliente();
if(c.isPresent()) {
_c = c.get();
_c.setId(cliente.getId());
_c.setNome(cliente.getNome());
}
final Cliente updatedCliente = clienteRepo.save(_c);
return ResponseEntity.ok(updatedCliente);
}
EDIT 2: PostMan request:
PostMan
You can check the mapped api in the log by adding the following config in the application.properties file:
logging.level.org.springframework.web.servlet.mvc.method.annotation=TRACE
For example:
I have a controller:
#RestController
#RequestMapping("/client")
public class HomeRestController {
#PutMapping("/{id}")
public void put(#PathVariable(value = "id") String id, #RequestBody TestingModel model) {
System.out.println(id);
System.out.println(model.getName());
}
}
When starting the application you can see the mapped API in the console log as below:
2020-07-14 09:36:49.287 TRACE 13224 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping :
c.e.e.c.HomeController:
{ /index}: home()
2020-07-14 09:36:49.288 TRACE 13224 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping :
c.e.e.c.HomeRestController:
{PUT /client/{id}}: put(String,TestingModel)
2020-07-14 09:36:49.293 TRACE 13224 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping :
o.s.b.a.w.s.e.BasicErrorController:
{ /error}: error(HttpServletRequest)
{ /error, produces [text/html]}: errorHtml(HttpServletRequest,HttpServletResponse)
Related
I am using
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>5.11.2</version>
<scope>test</scope>
</dependency>
for integration tests of a REST API. I've started with very basic expectations, to further ellaborate the tests later, once the minimum stuff passes the test. To my surpise, MockServer keeps telling me that no received requests match my expectations.
I am using the Java API, to write tests that use Mockito and PowerMock to deal with static methods. TestNG is the Test Freamework.
This is my code:
#PowerMockIgnore({"javax.xml.parsers.*", "org.apache.logging.log4j.*", "com.sun.org.apache.*", "sun.security.*", "javax.net.ssl.*"})
#PrepareForTest({K8sTarget.class, K8sApi.class})
public class DataAccessImplTest extends PowerMockTestCase {
private static final String HTTP_METHOD_GET = "GET";
private static final String USER_ID= "46756123123";
private static final String USERS_PATH = "/api/v1/users/%s";
private static final String CONTENT_TYPE_APP_JSON = "application/json";
#Mock
Target mockTarget;
#Mock
K8sClient mockK8sClient;
private DataAccessFactory dataAccessFactory;
private DataAccessImpl dataAccessUT;
private MockServerClient mockServer;
AutoCloseable closeable;
#BeforeClass
public void setup() {
// ensure all connection using HTTPS will use the SSL context defined by
// MockServer to allow dynamically generated certificates to be accepted
HttpsURLConnection.setDefaultSSLSocketFactory(
new KeyStoreFactory(new MockServerLogger()).sslContext().getSocketFactory());
this.mockServer = startClientAndServer(PortFactory.findFreePort());
this.closeable = MockitoAnnotations.openMocks(this);
dataAccessFactory = DataAccessFactory.getInstance();
assertNotNull(dataAccessFactory);
PowerMockito.mockStatic(K8sApi.class);
PowerMockito.mockStatic(K8sTarget.class);
PowerMockito.when(K8sApi.getK8sClient()).thenReturn(mockK8sClient);
PowerMockito.when(K8sTarget.of(Mockito.any(K8sClient.class), Mockito.any(Target.class))).thenReturn(mockTarget);
Mockito.when(mockTarget.getName()).thenReturn("localhost");
Mockito.when(mockTarget.getPort()).thenReturn(this.mockServer.getPort().intValue());
dataAccessUT = dataAccessFactory.createDataClient();
}
#BeforeMethod
public void prepareMocks() {
Mockito.when(mockTarget.getName()).thenReturn("localhost");
Mockito.when(mockTarget.getPort()).thenReturn(this.mockServer.getPort().intValue());
}
#AfterClass
public void teardown() throws Exception {
this.closeable.close();
this.mockServer.stop();
}
#Test
public void getUserTest_200_Ok() throws IOException {
dataAccessUT.getUserData(USER_ID);
mockServer.when(request()
.withMethod(HTTP_METHOD_GET)
.withPath(String.format(USERS_PATH, USER_ID))
)
.respond(
response()
.withStatusCode(HttpStatusCode.OK_200.code())
.withHeader(HttpHeaderNames.CONTENT_TYPE.toString(), CONTENT_TYPE_APP_JSON)
.withBody("some_response_body")
);
}
}
and these are the console logs:
10:06:24.067 [nioEventLoopGroup-2-1] DEBUG com.commonlibrary.httpclient.common.HttpConnectionListener:28 - 0.1 HttpConnectionListener::operationComplete: connected to [localhost:58136] from [/127.0.0.1:58204]
10:06:24.285 [MockServer-EventLog0] INFO org.mockserver.log.MockServerEventLog:108 - 58136 received request:
{
"method" : "GET",
"path" : "/api/v1/users/46756123123",
"headers" : {
"authorization" : [ "Bearer token" ],
"accept" : [ "application/json" ],
"host" : [ "localhost:58136" ],
"content-length" : [ "0" ]
},
"keepAlive" : true,
"secure" : false
}
10:06:24.350 [MockServer-EventLog0] INFO org.mockserver.log.MockServerEventLog:108 - 58136 no expectation for:
{
"method" : "GET",
"path" : "/api/v1/users/46756123123",
"headers" : {
"authorization" : [ "Bearer token" ],
"accept" : [ "application/json" ],
"host" : [ "localhost:58136" ],
"content-length" : [ "0" ]
},
"keepAlive" : true,
"secure" : false
}
returning response:
{
"statusCode" : 404,
"reasonPhrase" : "Not Found"
}
10:06:24.483 [MockServer-EventLog0] INFO org.mockserver.log.MockServerEventLog:108 - 58136 stopped for port: 58136
As you can see (unless I am missing something) request should match the expectation, but it doesn'. I have tried several things, all of them without success:
reduce the request expectation to the bare minimum, just calling request() without defining anything else. This shouls match EVERY incoming request. Same result.
introduce Times.exactly(1) in the expectation. Same result.
specify the headers I am sending in the request, even though my understanding is that if they are not set in the expectation, they are not used for matching. Same result.
After 2 days, I am running out of ideas, so any help or hint would be appreciated. Thanks!
Edition after following hint and checking code examples in MockServer site
Following #peter-rowth suggestion, I moved the request sent after creating expectations and it worked.
I am editing this issue also to make clear that it duplicates [https://stackoverflow.com/questions/63843619/mockserver-request-not-found][1], that I found later.
It looks like in your test you are creating the expectations in MockServer after executing the call to your code under test? The fact that your console output from MockServer does not output matched/not matched expectations (the default behavior) indicated to me that there are no expectations setup when the web request is made to MockServer and a 404 is default response by MockServer when there is no expectation for a request.
Try adding that expectation as the first line in your test.
I am using reactive MongoDb, and trying to implement Free Text search based on the weight
implementation("io.micronaut.mongodb:micronaut-mongo-reactive")
on below POJO
public class Product {
#BsonProperty("_id")
#BsonId
private ObjectId id;
private String name;
private float price;
private String description;
}
Tried this simple example
public Flowable<List<Product>> findByFreeText(String text) {
LOG.info(String.format("Listener --> Listening value = %s", text));
Flowable.fromPublisher(this.repository.getCollection("product", List.class)
.find(new Document("$text", new Document("$search", text)
.append("$caseSensitive", false)
.append("$diacriticSensitive", false)))).subscribe(item -> {
System.out.println(item);
}, error -> {
System.out.println(error);
});
return Flowable.just(List.of(new Product()));
}
I don't think this is the correct way of implementing the Free Text Search.
At first you don't need to have Flowable with List of Product because Flowable can manage more then one value unlike Single. So, it is enough to have Flowable<Product>. Then you can simply return the Flowable instance from find method.
Text search can be then implemented like this:
public Flowable<Product> findByFreeText(final String query) {
return Flowable.fromPublisher(repository.getCollection("product", Product.class)
.find(new Document("$text",
new Document("$search", query)
.append("$caseSensitive", false)
.append("$diacriticSensitive", false)
)));
}
Then it is up to the consumer of the method how it subscribes to the result Flowable. In controller you can directly return the Flowable instance. If you need to consume it somewhere in your code you can do subscribe() or blockingSubscribe() and so on.
And you can of course test it by JUnit like this:
#MicronautTest
class SomeServiceTest {
#Inject
SomeService service;
#Test
void findByFreeText() {
service.findByFreeText("test")
.test()
.awaitCount(1)
.assertNoErrors()
.assertValue(p -> p.getName().contains("test"));
}
}
Update: you can debug communication with MongoDB by setting this in logback.xml (Micronaut is using Logback as a default logging framework) logging config file:
<configuration>
....
<logger name="org.mongodb" level="debug"/>
</configuration>
Then you will see this in the log file:
16:20:21.257 [Thread-5] DEBUG org.mongodb.driver.protocol.command - Sending command '{"find": "product", "filter": {"$text": {"$search": "test", "$caseSensitive": false, "$diacriticSensitive": false}}, "batchSize": 2147483647, "$db": "some-database"}' with request id 6 to database some-database on connection [connectionId{localValue:3, serverValue:1634}] to server localhost:27017
16:20:21.258 [Thread-8] DEBUG org.mongodb.driver.protocol.command - 16:20:21.258 [Thread-7] DEBUG org.mongodb.driver.protocol.command - Execution of command with request id 6 completed successfully in 2.11 ms on connection [connectionId{localValue:3, serverValue:1634}] to server localhost:27017
Then you can copy the command from log and try it in MongoDB CLI or you can install MongoDB Compass where you can play with that more and see whether the command is correct or not.
I'm trying to send a PUT request from angular5 to to spring API , but i'm getting an error .
This is angular intervention.service.ts :
updateIntervention(id:number){
if(this.authService.getToken()==null) {
this.authService.loadToken();
}
return this.http.put(this.host+"/updateIntervention/"+id,
{headers:new
HttpHeaders({'Authorization':this.authService.getToken()})});
}
Intervention.component.ts
valider(ref: Intervention){
this.intervService.updateIntervention(ref.id)
.subscribe((data:any)=>{
console.log('there is no error ! ');
},err=>{
console.log('there is an error ! ');
})
ref.valid = !ref.valid;
}
In Spring-boot :
#RequestMapping(value="/updateIntervention/{id}",method = RequestMethod.PUT)
public Intervention update(#PathVariable Long id){
System.out.println("in intevention update");
Intervention I = new Intervention();
I = interventionRepo.getOne(id);
I.setValid(true); // it's boolean , this is the goal from this update
interventionRepo.save(I);
return I
}
As error i get in angular :
{"timestamp":1527443447949,"status":401,"error":"Unauthorized"}
As error In spring-boot :
access.AccessDeniedException: Access is denied
PS : this works when i send in angular both id and the object Ref , in spring , i write
public Intervention update(#PathVariable Long id , #RequestBody Intervention I){ ... }
But i don't need this as all what i want is to modify the attribut valid in the entity Intervention .
I'm using httpClient .
Any idea ?
The put method you are using has the following definition:
put(url: string, body: any | null, options)
You are providing the options object as a body parameter.And that's why you are getting unauthorized 401 which stands for "unauthenticated". Means that you have wrong or missing credentials.
You should change
return this.http.put(this.host+"/updateIntervention/"+id,
{headers:new
HttpHeaders({'Authorization':this.authService.getToken()})});
}
To:
return this.http.put(this.host+"/updateIntervention/"+id,
null,
{headers:new
HttpHeaders({'Authorization':this.authService.getToken()})});
}
I have a web application that should only be callable from specific IP addresses. Other than that, there is no need for authentication or for authorization; if you're coming from the right IP, you can see everything.
To that end, searching StackOverflow and other places, I found a number of suggestions for filtering requests by IP address in Spring Security. They all took this form (extending WebSecurityConfigurerAdapter using java configuration):
http.authorizeRequests().anyRequest().access("hasIpAddress('127.0.0.1/0')");
However, that never worked for me; it never rejected any request, no matter what IP address I made the request from. Instead, I implemented my IP filtering with a custom filter like this:
#Configuration
#EnableWebSecurity
#PropertySource("classpath:config.properties")
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
#Autowired
private Environment env;
#Override
protected void configure(HttpSecurity http) throws Exception {
String ipRange = env.getRequiredProperty("trusted_ips");
logger.info("##### SETTING UP SECURITY CONFIGURATION #############");
logger.info("## trusted_ips: " + ipRange);
logger.info("##### SETTING UP SECURITY CONFIGURATION - END #############");
http.addFilterBefore(new IPSecurityFilter(ipRange), J2eePreAuthenticatedProcessingFilter.class)
.authorizeRequests().antMatchers("/**").permitAll();
}
}
My IPSecurityFilter:
public class IPSecurityFilter extends OncePerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(IPSecurityFilter.class);
private String[] ipAddresses;
public IPSecurityFilter(String strIPAddresses) {
logger.info("#### Our IP Address whitelist: " + strIPAddresses);
this.ipAddresses = strIPAddresses.split(",");
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
logger.info("Checking whether request should be allowed: " + request.getRequestURI());
logger.info("### Request is coming from IP address: " + request.getRemoteAddr());
for (String ipAddress : ipAddresses) {
if (ipAddress.equals(request.getRemoteAddr())) {
logger.info("### Allowing request from ip address: " + request.getRemoteAddr());
return; // We accept requests from this IP address
}
}
// The remote IP address isn't on our white list; throw an exception
throw new AccessDeniedException("Access has been denied for your IP address: " + request.getRemoteAddr());
}
}
This seems to work in that the request is rejected if it originates from an IP Address that isn't on my white list.
However, with this configuration, my unit (using MockMvc) test fails; and it fails in a way that I would never have expected. When the unit test runs, it appears to use the Spring Security configuration correctly and the request passes the security test (the IP white list includes 127.0.0.1 and according to the log that is generated while the test is being run, the request is coming from that IP). However, the request never seems to be routed to my controller.
Here is my test:
#RunWith(SpringRunner.class)
#WebMvcTest()
//#WebMvcTest(value = HandlerController.class)
#AutoConfigureMockMvc
#Import(SecurityConfig.class)
public class HandlerControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void getIndex() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"services\":[\"OutboundMessageService\"]}", true));
}
}
And finally, here is my controller (please ignore the idiotic way that I'm generating the JSON return value, it's still very early in development):
#RestController
public class HandlerController {
private static final Logger logger = LoggerFactory.getLogger(HandlerController.class);
#RequestMapping("/")
public String index() {
logger.info("### handling a request for / ###");
return "{\"services\":[\"OutboundMessageService\"]}";
}
}
And here are the test results:
2017-11-14 08:29:12.151 INFO 25412 --- [ main] c.z.s.controllers.HandlerControllerTest : Starting HandlerControllerTest on 597NLL1 with PID 25412 (started by User in C:\Development\KnowledgeBin\NewArchitecture\OutboundMessageHandler)
2017-11-14 08:29:12.152 INFO 25412 --- [ main] c.z.s.controllers.HandlerControllerTest : No active profile set, falling back to default profiles: default
2017-11-14 08:29:12.178 INFO 25412 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Refreshing org.springframework.web.context.support.GenericWebApplicationContext#209da20d: startup date [Tue Nov 14 08:29:12 MST 2017]; root of context hierarchy
2017-11-14 08:29:13.883 INFO 25412 --- [ main] b.a.s.AuthenticationManagerConfiguration :
Using default security password: 56e3fab8-f7fb-4fbd-b2d2-e37eae8cef5e
2017-11-14 08:29:13.962 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : #### Our IP Address whitelist: 122.22.22.22,127.0.0.1
2017-11-14 08:29:14.086 INFO 25412 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#3f4f9acd, org.springframework.security.web.context.SecurityContextPersistenceFilter#470a9030, org.springframework.security.web.header.HeaderWriterFilter#60c16548, org.springframework.security.web.csrf.CsrfFilter#435ce306, org.springframework.security.web.authentication.logout.LogoutFilter#607b2792, com.zpaper.services.security.IPSecurityFilter#46baf579, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#27494e46, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#36453307, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#4bf324f9, org.springframework.security.web.session.SessionManagementFilter#452c8a40, org.springframework.security.web.access.ExceptionTranslationFilter#39ce27f2, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#5767b2af]
2017-11-14 08:29:14.183 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.zpaper.services.controllers.HandlerController.index()
2017-11-14 08:29:14.184 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/OutboundMessageService]}" onto public java.lang.String com.zpaper.services.controllers.HandlerController.outboundMessage()
2017-11-14 08:29:14.189 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-11-14 08:29:14.190 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-11-14 08:29:14.243 INFO 25412 --- [ main] c.z.s.config.HandlerWebConfiguration : #### My Configuration handler was called ####
2017-11-14 08:29:14.253 INFO 25412 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler]
2017-11-14 08:29:14.313 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for #ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext#209da20d: startup date [Tue Nov 14 08:29:12 MST 2017]; root of context hierarchy
2017-11-14 08:29:14.784 INFO 25412 --- [ main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring FrameworkServlet ''
2017-11-14 08:29:14.784 INFO 25412 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization started
2017-11-14 08:29:14.805 INFO 25412 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization completed in 21 ms
2017-11-14 08:29:14.897 INFO 25412 --- [ main] c.z.s.controllers.HandlerControllerTest : Started HandlerControllerTest in 3.095 seconds (JVM running for 3.995)
2017-11-14 08:29:14.981 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : Checking whether request should be allowed: /
2017-11-14 08:29:14.981 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : ### Request is coming from IP address: 127.0.0.1
2017-11-14 08:29:14.981 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : ### Allowing request from ip address: 127.0.0.1
MockHttpServletRequest:
HTTP Method = GET
Request URI = /
Parameters = {}
Headers = {Accept=[application/json]}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.363 sec <<< FAILURE! - in com.zpaper.services.controllers.HandlerControllerTest
getIndex(com.zpaper.services.controllers.HandlerControllerTest) Time elapsed: 0.12 sec <<< ERROR!
org.json.JSONException: Unparsable JSON string:
at org.skyscreamer.jsonassert.JSONParser.parseJSON(JSONParser.java:42)
As can be seen in the log messages, the IP filter is being invoked and is allowing the request to continue. However, the debug string that is being emitted in my handler is nowhere to be seen and the return body is blank. Can anyone tell me why my security filter would prevent MockMvc from having its request successfully routed to my controller?
Final Note: if I use the http.authorizeRequests().anyRequest().access("hasIpAddress('127.0.0.1/0')"); configuration that I first listed or completely get rid of Spring Security by removing my SecurityConfig class, the request is routed successfully to my handler.
I figured out how to make the test work. I was not able to find a single article that answered my question but by taking different suggestions from multiple blog posts, I came up with this which works for me:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = HandlerController.class)
public class HandlerControllerTest {
private MockMvc mvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
// mvc = MockMvcBuilders.standaloneSetup(new HandlerController()).build();
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void getIndex() throws Exception {
mvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"services\":[\"OutboundMessageService\"]}", true));
}
#Test
public void getMessageService() throws Exception {
mvc.perform(get("/OutboundMessageService").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"status\": \"SUCCESS\"}", true));
}
}
As you can see, I am no longer auto-wiring the MockMvc object and allowing it be automatically set up but am instead setting it up myself in the setUp() method. The commented-out line in the setUp() method works to successfully test my controller also but it doesn't route the request through my Spring Security IP address filter. I'm leaving it in so that users that don't need to test Spring Security can see an alternate method to set up the MockMvc object. The uncommented line sets up a MockMvc object such that it runs the request through both the security filters and my controller.
I know it is late but for others looking for the answer, you can add that filter to the MockMvc object like this:
#Autowired
private MockMvc mvc;
#Autowired
private YourCustomFilter filter;
#Before
public void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(filter).build();
}
Have looked at other related posts but nothing seemed to work.
Basically I'm trying to figure out how to pass data to my restful spring-boot app.
Here is the curl command I'm using:
$ curl -iX POST -H 'Content-Type: application/json' -d #args.json http://localhost:8080/myapp/dummyApi
With args.json contains:
file: args.json:
{
"arg1": "hello",
"arg2": 10,
"arg3": {
"a": "1",
"b": "2"
}
}
The api is defined in MyController.java as such:
file: MyController.java
#RestController
#RequestMapping("/myapp")
public class MyController {
//...
static class DummyRet {
private String foo;
public DummyRet(String f) {
foo = f;
}
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
#RequestMapping(value="/dummyApi", method=RequestMethod.POST)
public DummyRet dummyApi(#RequestParam(value = "arg1", required = false, defaultValue = "") String arg1,
#RequestParam(value = "arg2", required = false, defaultValue = "") Long arg2,
#RequestParam(value = "arg3", required = false) Map<String, String> arg3) {
LOGGER.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
LOGGER.info("Arguments received:");
LOGGER.info("arg1: " + arg1);
LOGGER.info("arg2: " + arg2);
LOGGER.info("arg3: " + arg3);
LOGGER.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
return new DummyRet("foo");
}
//...
}
The return of the curl command is 200 success (since non of the arguments is required) but the values of args are not reaching over to dummyApi method
$ curl -iX POST -H 'Content-Type: application/json' -d #args.json http://localhost:8080/myapp/dummyApi
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 04 Jun 2017 15:37:42 GMT
{"foo":"foo"}
The server console looks like this:
2017-06-04 18:17:56.818 DEBUG 32258 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing POST request for [/myapp/dummyApi]
2017-06-04 18:17:56.818 DEBUG 32258 --- [nio-8080-exec-7] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /myapp/dummyApi
2017-06-04 18:17:56.818 DEBUG 32258 --- [nio-8080-exec-7] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public com.xx.controllers.MyController$DummyRet com.xx.controllers.MyController.dummyApi(java.lang.String,java.lang.Long,java.util.Map<java.lang.String, java.lang.String>)]
2017-06-04 18:17:56.819 INFO 32258 --- [nio-8080-exec-7] c.p.controllers.MyController : >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
2017-06-04 18:17:56.819 INFO 32258 --- [nio-8080-exec-7] c.p.controllers.MyController : Arguments received:
2017-06-04 18:17:56.819 INFO 32258 --- [nio-8080-exec-7] c.p.controllers.MyController : arg1:
2017-06-04 18:17:56.819 INFO 32258 --- [nio-8080-exec-7] c.p.controllers.MyController : arg2: null
2017-06-04 18:17:56.819 INFO 32258 --- [nio-8080-exec-7] c.p.controllers.MyController : arg3: null
2017-06-04 18:17:56.819 INFO 32258 --- [nio-8080-exec-7] c.p.controllers.MyController : >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
2017-06-04 18:17:56.819 DEBUG 32258 --- [nio-8080-exec-7] m.m.a.RequestResponseBodyMethodProcessor : Written [com.xx.controllers.MyController$DummyRet#c35d46f] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#301ec38b]
2017-06-04 18:17:56.819 DEBUG 32258 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2017-06-04 18:17:56.819 DEBUG 32258 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : Successfully completed request
I hope I haven't left out any of the important details, but please let me know if anything is missing.
This worked:
I had two issues in my spring-boot java method,
I'm specifying #RrequestParam for the arguments, where I should be using #RequestBody instead
I have multiple of such method params where in POST it should be a single (see footnote) #RequestBody
But since I need to pass multiple arguments over to the method the solution was to wrap these arguments in a backing object, as such:
public static class Args {
// members
private String arg1;
private Long arg2;
private Map<String, String> arg3;
// accessors
public String getArg1() {return arg1;}
public void setArg1(String arg1) {this.arg1 = arg1;}
public Long getArg2() {return arg2;}
public void setArg2(Long arg2) {this.arg2 = arg2;}
public Map<String, String> getArg3() {return arg3;}
public void setArg3(Map<String, String> arg3) {this.arg3 = arg3;}
}
The receiver method would then be:
#RequestMapping(value="/dummyApi", method=RequestMethod.POST)
public DummyRet dummyApi(#RequestBody Args args) {
LOGGER.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
LOGGER.info("Arguments received:");
LOGGER.info("arg1: " + args.getArg1().toString());
LOGGER.info("arg2: " + args.getArg2().toString());
LOGGER.info("arg3: " + args.getArg3().toString());
LOGGER.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
return new DummyRet("foo");
}
Footnote: In fact it is possible to pass multiple params through custom implementation as described in this SO response. Haven't tried it
Refer below sample,
curl -H "Content-Type: application/json" --data #body.json
http://localhost:8080/ui/webapp/conf