unable to reach request mapping endpoint so im getting 404 error - java

I created a new controller with request mapping "/route" but no matter whatever i do I am unable to reach the endpoint localhost:8080/route. I always get 404.
How can I access the endpoint route without getting 404?
#RestController
public interface RouteController {
#ApiOperation(value = "Add a route details to adapter", nickname = "addroute", notes = "Add route details to the table and Send it to ATLAS", tags = {
"route", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successful Insertion into DB and Proceded for Transformation"),
#ApiResponse(code = 405, message = "Invalid Input") })
#RequestMapping(value = "/route", produces = { "application/json" }, consumes = {
"application/json" }, method = RequestMethod.POST)
public ResponseEntity<Void> createData(
#ApiParam(value = "Route Array", required = true) #Valid #RequestBody RouteArray routeArray) ;
}
Implementation class
public class RouteControllerImpl implements RouteController {
#Autowired
private RouteService routeService;
#Override
public ResponseEntity<Void> createData(#ApiParam(value = "Route Details", required = true) #Valid #RequestBody RouteArray routeArray) {
return new ResponseEntity<Void>(routeService.transformRoute(routeArray));
}
}

You need to change a few things in your implementation. You should not use #RestController with your OpenAPI api, but rather use #Api annotation. You should use #RestController on the class implementing this API.
#Validated
#Api(
value = "route",
description = The Controller API"
)
public interface RouteController {
#ApiOperation(value = "Add a route details to adapter", nickname = "addroute", notes = "Add route details to the table and Send it to ATLAS", tags = {
"route", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successful Insertion into DB and Proceded for Transformation"),
#ApiResponse(code = 405, message = "Invalid Input") })
#RequestMapping(value = "/route", produces = { "application/json" }, consumes = {
"application/json" }, method = RequestMethod.POST)
public ResponseEntity<Void> createData(
#ApiParam(value = "Route Array", required = true) #Valid #RequestBody RouteArray routeArray) ;
}
Implementation:
#RestController
public class RouteControllerImpl implements RouteController {
#Autowired
private RouteService routeService;
#Override
public ResponseEntity<Void> createData(#Valid RouteArray routeArray) {
return new ResponseEntity<Void>(routeService.transformRoute(routeArray));
}

Related

No 'Access-Control-Allow-Origin' header is present on the requested resource when sending delete request

I'm currently working on a website, which has a backend made in Java Spring Boot. But everytime i make a delete or a put request, the following Error appears in the console:
Access to fetch at 'http://10.0.10.67:8080/users/2' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I've tried multiple things, and nothing worked. I know it can't be a problem of the backend, because delete requests work, when sending them with postman.
This is my function for deleting users:
export async function deleteUser(id, token) {
console.log("helo")
const response = await fetch(`${URL}/users/${id}`, {
method: "DELETE",
mode: 'cors',
headers: {
"content-type": "application/json",
"authorization": `Bearer ${token}`,
"Access-Control-Allow-Origin": "http://localhost:3000"
}
})
if (!response.ok) {
return Promise.reject(response)
}
}
And this is my controller class in backend (like i said, the delete function works in backend, i tested it manually):
public class ApplicationUserController {
private final UserService userService;
private final TimeService timeService;
private final RfidChipService rfidChipService;
#Autowired
public ApplicationUserController(UserService userService, TimeService timeService, RfidChipService rfidChipService) {
this.userService = userService;
this.timeService = timeService;
this.rfidChipService = rfidChipService;
}
#Operation(summary = "Find ApplicationUser with a given firstname, lastname and/or email. If no parameters given, all users are returned.")
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "ApplicationUser(s) found",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = ApplicationUser.class))})})
#GetMapping()
public ResponseEntity<?> findUserByNameSurnameEmail(#Parameter(description = "Users firstname to search") #RequestParam(required = false) String firstname,
#Parameter(description = "Users lastname to search") #RequestParam(required = false) String lastname,
#Parameter(description = "Users email to search") #RequestParam(required = false) String email) {
try {
if (StringUtils.isNotBlank(firstname)) {
return ResponseEntity.ok(userService.getUserByFirstname(firstname));
} else if (StringUtils.isNotBlank(lastname)) {
return ResponseEntity.ok(userService.getUserByLastname(lastname));
} else if (StringUtils.isNotBlank(email)) {
return ResponseEntity.ok(userService.getUserByEmail(email));
}
return ResponseEntity.ok(userService.getAllUsers());
} catch (EntityNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No ApplicationUser(s) found");
}
}
#PostMapping(value = "/sign-up", consumes = "application/json")
#ResponseStatus(HttpStatus.CREATED)
public void signUp(#Parameter(description = "The new user to create") #Valid #RequestBody ApplicationUserDTO requestDTO) {
try {
List<RfidChipDTO> rfidChipDTOList = rfidChipService.getRfidChipWithNoUser();
requestDTO.setRfidChip(RfidChipMapper.fromDTO(rfidChipDTOList.get(0)));
userService.signUp(ApplicationUserMapper.fromDTO(requestDTO));
} catch (DataIntegrityViolationException e) {
throw new ResponseStatusException(HttpStatus.CONFLICT);
}
}
#Operation(summary = "Find a user by his id")
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "ApplicationUser found",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = ApplicationUser.class))}),
#ApiResponse(responseCode = "404", description = "ApplicationUser not found",
content = #Content)})
#GetMapping(path = "{id}")
public ResponseEntity<?> findById(#Parameter(description = "Id of user to get") #PathVariable Integer id) {
try {
return ResponseEntity.ok(userService.getById(id));
} catch (EntityNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "ApplicationUser could not be found");
}
}
#Operation(summary = "Find admins employees by his id")
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "Employees found",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = ApplicationUser.class))}),
#ApiResponse(responseCode = "404", description = "No Employees found",
content = #Content)})
#GetMapping(path = "{id}/employees")
public ResponseEntity<?> findEmployeesByAdminId(#Parameter(description = "Id of admin") #PathVariable Integer id) {
try {
return ResponseEntity.ok(userService.getUserByAdminId(id));
} catch (EntityNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Admin could not be found");
}
}
#Operation(summary = "Find users times by his id")
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "Time(s) found",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = ApplicationUser.class))}),
#ApiResponse(responseCode = "404", description = "No times found",
content = #Content)})
#GetMapping(path = "{id}/times")
public ResponseEntity<?> findTimesByUserId(#Parameter(description = "Id of user") #PathVariable Integer id) {
try {
return ResponseEntity.ok(timeService.findTimeByUserId(id));
} catch (EntityNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User could not be found");
}
}
#Operation(summary = "Update a user")
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "ApplicationUser was updated successfully",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = ApplicationUser.class))}),
#ApiResponse(responseCode = "409", description = "ApplicationUser could not be updated",
content = #Content),
#ApiResponse(responseCode = "400", description = "Validation failed",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = ApplicationUser.class))})})
#PatchMapping(value = "{id}", consumes = "application/json")
public ResponseEntity<?> update(#Valid #RequestBody ApplicationUserDTO applicationUserDTO, #PathVariable Integer id) {
try {
ApplicationUserDTO updatedUser = userService.update(applicationUserDTO, id);
return ResponseEntity.ok(updatedUser);
} catch (DataIntegrityViolationException e) {
throw new ResponseStatusException(HttpStatus.CONFLICT, "ApplicationUser could not be updated");
}
}
#Operation(summary = "Create a new ApplicationUser")
#ApiResponses(value = {
#ApiResponse(responseCode = "201", description = "ApplicationUser was created successfully",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = ApplicationUser.class))}),
#ApiResponse(responseCode = "409", description = "ApplicationUser could not be created",
content = #Content),
#ApiResponse(responseCode = "400", description = "Validation failed",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = ApplicationUser.class))})})
#ResponseStatus(HttpStatus.CREATED)
#PostMapping(consumes = "application/json")
public ResponseEntity<?> create(#Valid #RequestBody ApplicationUserDTO applicationUserDTO) {
try {
ApplicationUserDTO createdApplicationUserDTO = userService.create(applicationUserDTO);
return ResponseEntity.status(201).body(createdApplicationUserDTO);
} catch (DataIntegrityViolationException | ConstraintViolationException e) {
throw new ResponseStatusException(HttpStatus.CONFLICT, "ApplicationUser could not be created");
}
}
#Operation(summary = "Delete a user")
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "ApplicationUser was deleted successfully",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = ApplicationUser.class))}),
#ApiResponse(responseCode = "404", description = "ApplicationUser could not be deleted",
content = #Content)})
#DeleteMapping("{id}")
public ResponseEntity<?> delete(#PathVariable Integer id) {
try {
userService.deleteById(id);
return ResponseEntity.ok().build();
} catch (EmptyResultDataAccessException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "ApplicationUser could not be deleted");
}
}
}
I call the function in an "onClick(() => {}), and this seems to work.
I would appreciate it if someone could solve the problem for me.
Ps: I already tried the #CrossOrigin annotation, it didn't work
Sending a request from a browser is completely different that sending it with postman. You are not hitting directly your backend like postman, browsers does it for you
To understand it better you can read this one. crossorigin resource sharing
Your error comes from your backend configuration. You can use CorsConfigurer. Also you can combine it with spring security.
note: you can use allowedOrigins or allowerOriginsPattern according to your spring boot version.
spring boot enabling crossorigin
Let me know if I can help further.
I could fix the error, by creating a "configuration"-package and following class in it:
#Configuration
public class CorsConfiguration
{
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE");
}
};
}
}
This Class is global and it allows everyone access to put post delete and get requests on all controllers

Document a multipart/form-data endpoint with Springfox

I have a POST endpoint which receives a #ModelAttribute parameter. Everything is working ok, but the swagger documentation fails to have the descriptions, examples, etc.
I am using java 11, springboot 2.5.4 and springfox-boot-starter 3.0.0
Here is my code:
#Api
#RestController
#RequestMapping("/foo")
#Validated
public class MyRest {
#PostMapping(value = "/{id}/bar", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
#ApiOperation(value = "Do nothing", notes = "This endpoint does nothing")
public ResponseEntity<String> search(
#ModelAttribute MyModelRequest request,
#ApiParam(value = "Folder ID", required = true)
#PathVariable String id) {
// some business code
return new ResponseEntity<>("lorem ipsum", HttpStatus.OK);
}
}
MyModelRequest
#ApiModel
#Data
public class MyModelRequest {
#ApiParam(name = "fileName", value = "The name of the image to be stored in database")
#ApiModelProperty(value = "name model description", example = "summer picture", required = true)
private String name;
#DecimalMin("0.00")
#DecimalMax("100.00")
#ApiParam(name = "accuracy", value = "The required accuracy")
#ApiModelProperty(value = "Minimum required accuracy", example = "95.15", required = false)
private BigDecimal accuracy;
#ApiParam(name = "marginTop", value = "Top margin of the image")
#ApiModelProperty(value = "Separation between top item and the image", example = "300", required = false)
private Integer marginTop;
#ApiParam(name = "image")
#ApiModelProperty(value = "The image to be stored", example = "vacations.png", required = true)
private MultipartFile image;
}
And this is the generated swagger doc
UPDATE: I noticed that if I change the consumes = { MediaType.MULTIPART_FORM_DATA_VALUE } for consumes = { MediaType.APPLICATION_JSON_VALUE } or remove the whole "consumes" parameter from the endpoint, the documentation shows up correctly, however, doing this will make the fileupload fail.

Have controller methods to getAll, getById and getByEmail in the same controller while following RESTful API standards

I have an entity Users, which contains id and emailAddress. I want to write a controller which has 3 get methods simultaneously:
getAll() - http://localhost:8080/api/users?pageNumber=0&pageSize=10
get(UUID id) - http://localhost:8080/api/users/209347293875928375928
get(String email) - http://localhost:8080/api/users/fireball%40email.com or http://localhost:8080/api/users?emailAddress="fireball%40email.com"
NOTE: The difference in the two pieces of code is the arguments of the last function in both(The first is #PathVariable and the second is a #RequestParam.
I have tried two ways to accomplish this(both run into a separate issue), the first being:
#GetMapping
#ApiOperation(value = "List all Users")
#ApiResponses({
#ApiResponse(code = 200, message = "OK", response = User.class, responseContainer = "List"),
#ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
#ApiResponse(code = 404, message = "Not Found", response = Error.class),
#ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
})
#Transactional(readOnly = true)
public ResponseEntity<Page<User>> getAll(
#RequestParam(required = false, defaultValue = "0") int pageNumber,
#RequestParam(required = false, defaultValue = "10") int pageSize) {
return ResponseEntity.ok(UserService.getUsers(pageNumber, pageSize));
}
#GetMapping(path = "/{id}")
#ApiOperation(value = "Get User by ID")
#Transactional(readOnly = true)
#ApiResponses({
#ApiResponse(code = 200, message = "OK", response = User.class),
#ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
#ApiResponse(code = 404, message = "Not Found", response = Error.class),
#ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
})
public ResponseEntity<?> get(#PathVariable("id") final UUID id) {
return UserService.get(id)
.map(ResponseEntity::ok)
.map(ResponseEntity.class::cast)
.orElse(
ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, id))));
}
#GetMapping
#ApiOperation(value = "Get User by Email Address")
#Transactional(readOnly = true)
#ApiResponses({
#ApiResponse(code = 200, message = "OK", response = User.class),
#ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
#ApiResponse(code = 404, message = "Not Found", response = Error.class),
#ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
})
public ResponseEntity<?> get(#RequestParam("email") final String email) {
return UserService.get(email)
.map(ResponseEntity::ok)
.map(ResponseEntity.class::cast)
.orElse(
ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, email))));
}
The above fails at compilation with Ambiguous mapping. Cannot map 'userController' method as error. Essentially getAll() and get(#RequestParam("email") final String email) have the same URL path - /users.
The second being:
#GetMapping
#ApiOperation(value = "List all Users")
#ApiResponses({
#ApiResponse(code = 200, message = "OK", response = User.class, responseContainer = "List"),
#ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
#ApiResponse(code = 404, message = "Not Found", response = Error.class),
#ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
})
#Transactional(readOnly = true)
public ResponseEntity<Page<User>> getAll(
#RequestParam(required = false, defaultValue = "0") int pageNumber,
#RequestParam(required = false, defaultValue = "10") int pageSize) {
return ResponseEntity.ok(UserService.getUsers(pageNumber, pageSize));
}
#GetMapping(path = "/{id}")
#ApiOperation(value = "Get User by ID")
#Transactional(readOnly = true)
#ApiResponses({
#ApiResponse(code = 200, message = "OK", response = User.class),
#ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
#ApiResponse(code = 404, message = "Not Found", response = Error.class),
#ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
})
public ResponseEntity<?> get(#PathVariable("id") final UUID id) {
return UserService.get(id)
.map(ResponseEntity::ok)
.map(ResponseEntity.class::cast)
.orElse(
ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, id))));
}
#GetMapping(path = "/{email}")
#ApiOperation(value = "Get User by Email Address")
#Transactional(readOnly = true)
#ApiResponses({
#ApiResponse(code = 200, message = "OK", response = User.class),
#ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
#ApiResponse(code = 404, message = "Not Found", response = Error.class),
#ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
})
public ResponseEntity<?> get(#PathVariable("email") final String email) {
return UserService.get(email)
.map(ResponseEntity::ok)
.map(ResponseEntity.class::cast)
.orElse(
ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, email))));
}
Here I run into the issue of the controller not being able to resolve between get(#PathVariable("email") final String email and get(#PathVariable("id") final UUID id) with the following error:
Ambiguous handler methods mapped for '/api/gems/users/fireball%40email.com': {public org.springframework.http.ResponseEntity com.personal.project.controllers.UserController.get(java.util.UUID), public org.springframework.http.ResponseEntity com.personal.project.controllers.UserController.get(java.lang.String)}
You can solve it by merging these two Ambiguous path into one.
For the first case:
#GetMapping
#ApiOperation(value = "List all Users")
#ApiResponses({
#ApiResponse(code = 200, message = "OK", response = User.class, responseContainer = "List"),
#ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
#ApiResponse(code = 404, message = "Not Found", response = Error.class),
#ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
})
#Transactional(readOnly = true)
public ResponseEntity<?> getUsers(
#RequestParam(required = false, defaultValue = "0") int pageNumber,
#RequestParam(required = false, defaultValue = "10") int pageSize,
// add email as param.
#RequestParam(required = false) String email,
) {
if(email ==null || StringUtils.isEmpty(email)){
return ResponseEntity.ok(UserService.getUsers(pageNumber, pageSize));
}else return UserService.get(email)
.map(ResponseEntity::ok)
.map(ResponseEntity.class::cast)
.orElse(
ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, email))));
}
For the second one:
// change the type UUID id to string.
#GetMapping(path = "/{id}")
...
public ResponseEntity<?> get(#PathVariable("id") final String id) {
// check if id as an uuid or email, and based on that take action
}
In line with my previous answer, you only need two controller methods. Then, in the method that handles GET /users requests, the presence or absence of the name query parameter will determine whether the server will fetch all users or filter the user by email.
See below the example below. For simplification, I have not added the OpenAPI annotations and also removed pagination, but you can easily add them as you need:
#RestController
#RequestMapping(path = "/users",
produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
#GetMapping
public ResponseEntity<List<User>> findUsers(#RequestParam("email") final String email) {
// If email is null/empty/blank, then fetch all users
// Otherwise filter users by email
}
#GetMapping(path = "/{id}")
public ResponseEntity<User> findUserById(#PathVariable("id") final UUID id) {
// Find user with the given ID
}
}
In case you plan to support multiple filters (or simply want to avoid if-'else's or custom repository methods), it may be a good idea to use Query by Example (QBE), which is supported by Spring Data:
Query by Example (QBE) is a user-friendly querying technique with a simple interface. It allows dynamic query creation and does not require you to write queries that contain field names. In fact, Query by Example does not require you to write queries by using store-specific query languages at all.
The documentation also states the following:
The Query by Example API consists of three parts:
Probe: The actual example of a domain object with populated fields.
ExampleMatcher: The ExampleMatcher carries details on how to match particular fields. It can be reused across multiple Examples.
Example: An Example consists of the probe and the ExampleMatcher. It is used to create the query.
Query by Example is well suited for several use cases:
Querying your data store with a set of static or dynamic constraints.
Frequent refactoring of the domain objects without worrying about breaking existing queries.
Working independently from the underlying data store API.
Query by Example also has several limitations:
No support for nested or grouped property constraints, such as firstname = ?0 or (firstname = ?1 and lastname = ?2).
Only supports starts/contains/ends/regex matching for strings and exact matching for other property types.
See below how to use Query by Example in your case:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class UserFilter {
private String email;
}
#RestController
#RequiredArgsConstructor
#RequestMapping(path = "/users",
produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
private final UserService userService;
#GetMapping
public ResponseEntity<List<User>> findUsers(#RequestParam("email") final String email) {
UserFilter filter = UserFilter.builder().name(email).build();
List<User> users = userService.findUsers(filter);
return ResponseEntity.ok(users);
}
#GetMapping(path = "/{id}")
public ResponseEntity<User> findUserById(#PathVariable("id") final UUID id) {
User user = userService.findUserById(filter).orElseThrow(SomeSortOfException::new);
return ResponseEntity.ok(user);
}
}
#Service
#RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public List<User> findUsers(UserFilter filter) {
User user = new User();
user.setEmail(filter.getEmail());
Example<User> example = Example.of(user);
return userRepository.findAll(example);
}
public Optional<User> findUserById(UUID id) {
return userRepository.findById(id);
}
}

Update a response object of the end-point

I generated automatically SpringMVC API using swagger. Now I want to update some end-points manually.
I have the folloiwng end-point:
#ApiOperation(value = "Estimation of ...", notes = "...", response = Similarity.class, responseContainer = "List")
#io.swagger.annotations.ApiResponses(value = {
#io.swagger.annotations.ApiResponse(code = 200, message = "Similarity metrics", response = Similarity.class),
#io.swagger.annotations.ApiResponse(code = 200, message = "Unexpected error", response = Similarity.class) })
#RequestMapping(value = "/estimateSimilarity",
produces = { "application/json" },
method = RequestMethod.GET)
public ResponseEntity<HashMap<String,Double>> estimateSimilarity(
#ApiParam(value = "...", required = true)
#RequestParam(value = "term1", required = true) String term,
#ApiParam(value = "...", required = true)
#RequestParam(value = "terms", required = true) List<String> concepts)
throws NotFoundException {
Similarity similarity = new Similarity();
HashMap<String,Double> result = similarity.getEstimates(term1, terms);
return new ResponseEntity<HashMap<String,Double>>(HttpStatus.OK);
}
Instead of response = Similarity.class, I want to return HashMap<String,Double> result. How should I update the above-given code to be able to return this object?
Try modifying the ApiOperations Response container.
#ApiOperation(value = "Estimation of ...", notes = "...", response = Double.class, responseContainer = "Map")

Two Spring Controller Methods, One Returns 200, Other Returns 404. Only Differ By The Mapping URL

I have two controller methods shown below, the /garages/a works when I post JSON to it (response is a 200) but the /garages/a/brand gives a 404.
They only differ by the mapping value.
I am using Spring 4.1.4.RELEASE and Java config. I am not using Spring Boot.
Controller methods:
#RequestMapping(value = "/garages/a", method = RequestMethod.POST, headers = "Content-Type = application/json", produces = "application/json")
public Garage specificGaragePost(
#RequestBody Garage garage) {
return garage;
}
#RequestMapping(value = "/garages/a/brand", method = RequestMethod.POST, headers = "Content-Type = application/json", produces = "application/json")
public Garage specificGarageBrandPost(
#RequestBody Garage garage) {
return garage;
}
Garage class
public class Garage {
private String id;
private String brand;
private List<Fuel> fuels;
private double[] location;
//Getters and setters left out for brevity
Example JSON (works for the /garages/a URL but not /garages/a/brand)
{
"id": "some-id",
"brand": "brand",
"fuels": [],
"location": [0,0]
}
The problem is in the #RequestMapping sintax. Remove the blanks in the headers section:
#RequestMapping(value = "/garages/a", method = RequestMethod.POST, headers = "Content-Type=application/json", produces = "application/json")
#RequestMapping(value = "/garages/a/brand", method = RequestMethod.POST, headers = "Content-Type=application/json", produces = "application/json")
Even better, instead of using headers for filtering the Content-Type, use consumes which is specific for that purpose:
#RequestMapping(value = "/garages/a", method = RequestMethod.POST, consumes= "application/json", produces = "application/json")
#RequestMapping(value = "/garages/a/brand", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
Because when it finds /garages/a/brand, it treats it as /garages/a/ with a parameter brand, but /garages/a/ doesn't accept any parameters.

Categories