I seem to be butting heads with a limiter somewhere. One of my Spring-Boot REST endpoint (POST) parameters (surveyResults) is looking for a string of JSON:
private static final String SURVEY_RESULTS_ENDPOINT = "/survey/results";
#PostMapping(
value = SURVEY_RESULTS_ENDPOINT,
produces = { "application/hal+json", "application/json" }
)
#ApiOperation(value = "Save one survey results")
public Resource<SurveyResult> createSurveyResults(
#ApiParam(value = "who/what process created this record", required = true) #Valid
#RequestParam(value = "recordCreatedBy", required = true) String createdBy,
#ApiParam(value = "was an issue identified", required = true)
#RequestParam(value = "hadFailure", required = true) Boolean hadFailure,
#ApiParam(value = "JSON representation of the results", required = true)
#RequestParam(value = "surveyResults", required = true) String surveyResult
) ...
If I post to this with about 1500 characters, it works. Somewhere just over that and it will fail with a HTTP 400 error bad request. The whole payload is less than 2K with the other parameters.
I just moved from Wildfly to a new server setup. My company is adopting continuous deployment to cloud servers so i don't have much control nor visibility to this new load balanced server. The server is "server": "openresty/1.13.6.2" - any idea what limit I am running into?
Please use #RequestBody instead of #RequestParam.
#RequestBody annotation maps the HTTP request's body to an object. #RequestParam maps the request parameter in the request, which is in the URL and not in the body.
Most browsers have a limitation to the number of characters supported in a request parameter, and you just hit that limit.
What I would suggest is to create a POJO that looks like this
public class Body {
private String createdBy;
private Boolean hadFailure;
private String surveyResult;
// getters and setters
}
Now your controller will be simpler
#PostMapping(
value = SURVEY_RESULTS_ENDPOINT,
produces = { "application/hal+json", "application/json" }
)
public Resource<SurveyResult> createSurveyResults(#RequestBody Body body) {
}
Wherever you are posting, you will have to now post a JSON (Content-Type = application/json) that looks like the following
{ "createdBy" : "foo", "hadFailure" : false, "surveyResult" : "the foo bar"}
Related
I have interfaces generated by Swagger Codegen. It looks like this:
#PostMapping(value = "/ipc/conf", produces = {"application/json", "application/problem+json"}, consumes = {
"application/json"})
default ResponseEntity<CustomResponseEntity> ipcConfPost(
#ApiParam(value = "ID", required = true) #RequestHeader(value = "X-Request-ID", required = true) String xRequestID,
#ApiParam(value = "Value for identifying a single transaction across multiple services up to the backend.", required = true) #RequestHeader(value = "X-Correlation-ID", required = true) String xCorrelationID,
#ApiParam(value = "The payload to transmit", required = true) #Valid #RequestBody IPcData ipcConfData,
#ApiParam(value = "The business context is a general classification for a larger number of requests.") #RequestHeader(value = "X-Business-Context", required = false) String xBusinessContext) {
getRequest().ifPresent(request -> {
for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "{ \"id\" : \"id\", \"error\" : \"error\" }";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
In the implementation I want to have a full list of request headers (I need some of them in the response) or to be able to get a value of a header that is not listed in the API. The thing is I cannot change the signature of the endpoint since it will cause a major headache in further releases.
So is there any way to achieve this?
You have the request object in your code already so you can get the headers from it. i.e. request.getHeaderNames() then loop through them.
After that, you can add them to the response with
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("key", "value");
ResponseEntity.ok().headers(responseHeaders).body("some body");
I'm new to spring boot and trying to make a Rest API with some resources, I have the following:
Get all existing rules:
#GetMapping("/rules")
#ResponseStatus(HttpStatus.OK)
#ApiOperation(value = "Get all the existing rules", response = RuleViewModel.class) //Swagger documentation
public ResponseEntity<Page<RuleViewModel>> get(#PageableDefault(page = 0, size = 25) Pageable pageable) {
Page<Rule> rulesEntity = ruleService.findByDeletedIsSend(false, pageable);
Page<RuleViewModel> ruleViewModels = rulesEntity.map(mapper::ruleViewModelMapper);
return new ResponseEntity<>(ruleViewModels, HttpStatus.OK);
}
Filter all the existing rules with different parameters
#GetMapping(value = "/rules")
#ResponseStatus(HttpStatus.OK)
#ApiOperation(value = "Get a specific rule by filter", response = RuleViewModel.class)
public ResponseEntity<Page<RuleViewModel>> getByFilter(
#RequestParam(name = "id", required = false) Optional<String> ruleId,
#RequestParam(name = "description", required = false) Optional<String> description,
#RequestParam(name = "types", required = false) Optional<List<Boolean>> types,
#RequestParam(name = "layers", required = false) Optional<List<Layer>> layers,
#RequestParam(name = "groups", required = false) Optional<List<Category>> groups,
Pageable pageable
) {
Page<Rule> rulesEntities = ruleService
.filterRule(
types.orElse(Arrays.asList(true, false)),
layers.orElse(layerService.findByIsActive(true)),
groups.orElse(categoryService.findByIsActive(true)),
description.orElse(""),
ruleId.orElse(""),
false,
pageable
);
Page<RuleViewModel> ruleViewModels = rulesEntities.map(mapper::ruleViewModelMapper);
return ruleViewModels.getSize() > 0 ? new ResponseEntity<>(ruleViewModels, HttpStatus.OK) : new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
As you can see the path is the same ("/rules") but in the case of the filters, it adds all the optional parameters.
The problem I have is that when I want to use filter params as:
http://localhost:5656/v1/rules?id=7be4336d-6495-5b71-9bc2-a97c9da5ede2
It always goes to get all rules endpoint.
The workaround I made was to add the "search" path to URI like this:
http://localhost:5656/v1/rules/search?id=7be4336d-6495-5b71-9bc2-a97c9da5ede2
So the requests get correctly mapped, but I think it violates the Rest best practices.
Could you point me in the right way to achieve this?. Thanks in advance.
Since both the methods have the same return type and all parameters to the getByFilter method are optional, why don't you remove the getByFilter method and add the parameters as optional parameters to the get method? The get method can then perform the filter logic if any of the optional parameters are passed in, else return the complete response without any filtering.
I have a rest java webservice using a PUT method for updating an employee.
The web service should take a list of files and a json containig the infos to be updated.
The method has the following signature
#RequestMapping(value = "/employees/{employeeId}", method = RequestMethod.PUT, consumes =
MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> updateEmployee(
#RequestPart EmployeeUserUpdateDto employeeDto,
#PathVariable("employeeId") int employeeId,
#RequestPart MultipartFile[] uploadedFiles, BindingResult bindingResult)
When i test this request with postman like follows it says that my json is not present:
PS : I have a similar post request for adding an employee, working just fine with the same configuration , the sole diffrence between the 2 requests is the pathvariable and PUT not POST
add over method : #ResponseBody
You can get multipart request in dto wrapper like this:
#PutMapping("/upload-poc")
ResponseEntity<?> uploadFileWithData(#ModelAttribute TestModel model){
log.info("File Exist: {}",model.getImage().get(0).getOriginalFilename());
log.info("Data: {}", model);
return new ResponseEntity<>(HttpStatus.OK);
}
#Getter
#Setter
#ToString
class TestModel{
private List<MultipartFile> images;
//#NotNull(message = "Name must be provided")
#Size(min = 2, max = 6)
private String name;
private Long id;
}
I am uploading 3 files in one request.
Here is the post man request sample:
I want to get the body values from received html request body using Spring boot:
#PostMapping(value = "/v1/notification")
public ResponseEntity<String> handleNotifications(
#RequestParam(value = "uniqueid", required = false)) String uniqueidValue,
#RequestParam(value = "type", required = false)) String statusValue) {
// Get values from html body
return new ResponseEntity<>(HttpStatus.OK);
}
For example when I receive into the notification body:
some_key=some_value&sec_key=sec_value
I would like to parse the values. How I can implement this?
You can take the key value pair request with using Map and #RequestBody as below:
#PostMapping(value = "/v1/notification")
public ResponseEntity handleNotifications(#RequestBody Map<String,String> keyValuePairs) {
// here you can use keyValuePairs
// you can process some specific key like
String value = keyValuePairs.get("someSpecificKey");
return ResponseEntity.ok(value);
}
Here I attach example postman request :
Requirement: I have a POST method which takes the input JSON as a String and passes it to another microservice. I don't want to create an Object (Bean) of this input JSON.
method:
#ApiOperation(notes = "example" value = "/example", consumes = ".." , method= "..")
#RequestMapping(name = "xxx" value ="/hello" ..)
#ApiResponses(..)
public #ResponseBody String getXXX (#Apiparam(name="JSONrequest", required = true) #RequestBody String JSONrequest){
}
Problem:
The generated Swagger doesn't show the input as a JSON model where all the JSON attributes are displayed.
Expectation:
I want to display my Swagger Something like this :
Definately I am missing the key thing. Any thoughts?
If changing from String to a concrete object is not okay (although that's what I would recommend you to do since it's cleaner), you can try using #ApiImplicitParams (check out their documentation)
#ApiOperation(notes = "example" value = "/example", consumes = ".." , method= "..")
#ApiImplicitParams({
#ApiImplicitParam(name = "Object", value = "Object to be created", required = true, dataType = "your.package.BodyClass", paramType = "body")
})
#RequestMapping(name = "xxx" value ="/hello" ..)
#ApiResponses(..)
public #ResponseBody String getXXX (#Apiparam(name="JSONrequest", required = true) #RequestBody String JSONrequest){
}
(not sure if you still need the #Apiparam(name="JSONrequest", required = true) bit from the method parameter)
It's an old question but since I haven't found a solution online here how I to customized the example value in the swagger documentation produce automatically by the java annotations.
I use swagger 2.0 and springfox.version 2.10.5.
The Idea is documenting the class of the request parameter that has the #RequestBody annotation. for example my method is
#ApiOperation(
value = "Start ListBuilder extraction",
response = ExtractionLogEntity.class,
produces = "application/json"
)
#PostMapping("/extraction/start")
public ExtractionLogEntity startTask(
#RequestBody(required = true) ExtractionRequest request,
In order to expose request json object example I added a #ApiModelProperty(example = "...") annotation to the properties of ExtractionRequest .
#ApiModelProperty(example = "[{ 'field':'value'}]")
#NotNull
private List<ListBuilderFieldEntity> fields;
#ApiModelProperty(example = "1000")
private String ied;
#ApiModelProperty(example = "US")
private String codebase;
And that's the result
I had the similar issue. My Service Class takes #RequestBody argument in String.
So, what I did :
Created a POJO and used #RequestBody annotation with it instead of inputString.
#RequestMapping(value = "/api/entity/{entityId}/user/query", method = {RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
ResponseEntity<String> queryUser(#PathVariable("entityId") String entityId,
#RequestBody QueryUserJsonSchemaPOJO queryUserJsonSchemaPOJO, String inputString,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
return userService.queryUserService(inputString, entityId, request);
}
Created an AOP with #Around annotation which update the inputString argument.
#Around(value = "execution(* com.athmin.rest.UserController.*(..)) || execution(* com.athmin.rest.CityController.*(..)), and args(..) " +
" && #annotation(com.athmin.annotations.JSONSchemaFileName) ")
public Object validateRequestBodyAgainstJsonSchema(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object[] modifiedArgs = proceedingJoinPoint.getArgs();
for (Object o : proceedingJoinPoint.getArgs()) {
if (o instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) o;
requestBody = httpServletRequest.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}
});
for (int i = 0; i < modifiedArgs.length; i++) {
if (modifiedArgs[i] == null) { // Only inputString is null in my case
modifiedArgs[i] = requestBody;
}
}
proceedingJoinPoint.proceed(modifiedArgs);
}